Preface: I have two models (Product and UserProfile) and I would to implement a mine comments system. The comment is related to an object, Product or UserProfile.
class Product(models.Model):
name = models.CharField(max_length = 40)
comments = models.ManyToMany(Comment)
class UserProfile(models.Model):
user = models.ForeignKey(User, unique = True)
comments = models.ManyToMany(Comment)
class Comment(models.Model):
text = models.TextField()
author = models.ForeignKey(User)
timestamp = models.DateTimeField(auto_now_add = True)
Is it correct the logic under these models? I'm doubtful, because this way means a Product can has many comments (and it's correct) but also a Comment can has many products (I don't think it's correct).
It isn't?
Your comment should have have a ForeignKey to the UserProfile or Product i.e. A single comment can only belong to a single product or user profile, but a user profile/product can have many different comments
def Comment(models.Model):
profile = models.ForeignKey(UserProfile)
product = models.ForeignKey(Profile)
Obviously this isn't ideal as there are two relationships you need to manage, and sometimes you will only want to use one etc.
To get over this, you can use a Generic Foreign Key:
https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/#generic-relations
this allows you to link a comment to any type of content (products, user profiles and more) without having to specify the models up front
def Comment(models.Model):
...
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
Related
Tried to figure this out on my own but stumped -
I'm working on a crm project to learn Django and have gotten stuck trying to incorporate activities between a user and client. Specifically, I'm trying to make it possible to record an email interaction and to have the from/to fields reference either a user or client model. So essentially an email can be recorded as either from a user to client or vice versa. The next part would be to allow for multiple clients or users to be tagged in the correct fields of this interaction.
I've tried incorporating the to and from fields as models so that they can use the GenericForeignKey class like so:
class Activity(models.Model):
owner = models.ForeignKey(User, on_delete=models.DO_NOTHING)
date = models.DateTimeField()
class EmailTo(models.Model):
content_type = models.ForeignKey(ContentType, on_delete=models.DO_NOTHING)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type')
class EmailFrom(models.Model):
content_type = models.ForeignKey(ContentType, on_delete=models.DO_NOTHING)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type')
class EmailActivity(Activity):
emailto = models.ForeignKey(EmailTo, on_delete=models.DO_NOTHING)
emailfrom = models.ForeignKey(EmailFrom, on_delete=models.DO_NOTHING)
body = models.TextField(blank=True)
but now I'm stuck trying to figure out how to represent that on a form. I thought maybe I could use a union to combine two queries into one field using a ModelMultipleChoiceField:
class EmailActivityForm(forms.ModelForm):
emailto = forms.ModelMultipleChoiceField(
queryset=Client.objects.all().union(User.objects.all()),
label="To")
emailfrom = forms.ModelMultipleChoiceField(
queryset= Client.objects.all().union(User.objects.all()),
label="From")
body = forms.CharField(widget=forms.Textarea)
class Meta:
model = EmailActivity
exclude = '__all__'
but see now that this is not possible since the queries are not the same size.
I'm starting to think I need to go back to my user models and make users and clients inherit from one "Person" model or something similar. Wanted to check here first though to see if I was possibly missing something.
you better write your own manual form
class EmailActivityForm(forms.Form):
emailto = forms.ModelMultipleChoiceField(queryset=Client.objects.all().union(User.objects.all()), label="To")
emailfrom = forms.ModelMultipleChoiceField(queryset= Client.objects.all().union(User.objects.all()), label="From")
body = forms.CharField(widget=forms.Textarea)
and keep going with all the other fields left.
by the way, the forms.ModelForms is made for a single model, not multiple
I have a model:
class Product(models.Model):
title = models.CharField(max_length=200)
url = models.URLField()
pub_date = models.DateTimeField()
votes_total = models.IntegerField(default=1)
image = models.ImageField(upload_to='images/')
icon = models.ImageField(upload_to='images/')
body = models.TextField()
hunter = models.ForeignKey(User, on_delete=models.CASCADE)
Now I'd like to add a functionality of upvoters to know on what products user has already voted. I need this to allow users vote on the one product only once.
Again, to clarify - user can vote on several products but only once on each.
So the relation is one product - many users (upvoters).
I tried to add the next field but cannot make a migration even if default field is provided. Also I tried to clear the database but again cannot make it work.
upvoters = models.ForeignKey(User, on_delete=models.CASCADE, related_name='upvoted')
I suppose it works the next way:
Field to determine upvoted products.
To check if user has been upvoted on product, call: User.upvoted.filter(id=product.id).count() == 1
This means that user has already upvoted on this product.
What's wrong? What should I change to make it work?
You will have to use ManyToMany, but you can use a custom through model to restrict the product/vote combinations.
To Product class, add:
voters = models.ManyToManyField(User, through='ProductVote', related_name='product_voters')
Then add the custom through model:
class ProductVote(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
product = models.ForeignKey(Vote, on_delete=models.CASCADE)
class Meta:
unique_together = ['user', 'product']
If you try to add a vote for the same user/product combination, an IntegrityError will be raised.
I have the following models:
class UserPost(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
class User(AbstractUser):
MALE = 'M'
FEMALE = 'F'
GENDER_CHOICES = (
(MALE, 'Male'),
(FEMALE, 'Female')
)
posts = models.ManyToManyField(Post, through='UserPost')
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class Post(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
content = models.TextField()
status = models.CharField(max_length=100)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
When I run python manage.py makemigrations, it raises the following error:
users.User.posts: (fields.E303) Reverse query name for 'User.posts' clashes with field name 'Post.user'.
HINT: Rename field 'Post.user', or add/change a related_name argument to the definition for field 'User.posts'.
There is a many-to-many relationship between User and Post models. Each user can like many posts and each post can be liked by many users.
There is also a many-to-one relationship between User and Post models. Each user can write many posts and each post can be written by only one user.
Shouldn't reverse query name for 'User.posts' be user_set by default. If so, why is this name clashing with field name 'Post.user'? Can someone explain the meaning of this error? Thanks.
Do you need the UserPost model? It looks to have all the same fields as Post, and if you're after efficient querying, Django automatically creates database indexes on foreign keys. Here's a simple setup that should work pretty well:
class User(AbstractUser):
# Your fields go here, but you might not need the posts field
class Post(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='posts')
This would let you do a user.posts.all() to get all of the Post instances that belong to that user.
I'm new to Django so I make 3 simple tables to return a WishList. The thing is that I want whenever user asks for WishList, his/her user_id is used to make a SELECT query to return his/her own WishList. And I want to get product title and product url from my WishList table. I'm using to_field but with that way I only can get product title back. I don't know much about Django so help me!
Product
class Product(models.Model):
class Meta:
unique_together = (('id', 'title'),)
title = models.CharField(max_length=200, unique=True,
help_text='Name of the product')
url = models.CharField(max_length=300, default='',
help_text='Url of the product')
def __str__(self):
return 'Product: {}'.format(self.title)
WishList
class WishList(models.Model):
class Meta:
unique_together = (('user', 'product'),)
user = models.ForeignKey(fbuser,
on_delete=models.CASCADE,
help_text='Facebook user',
to_field='user_id')
product = models.ForeignKey(Product, to_field='title', db_column='title',
on_delete=models.CASCADE)
def __str__(self):
return 'WishList: {}'.format(self.user)
It's not a good practice to override to_field to another field different than your model.pk unless you have a really good reason and you know what you are doing (definitely not the case right now).
So after you read the docs, you will know that in order to get wishlisht related to a user, you can use the ForeignKey reverse relation to get all related wishlists for a user.
user_wishlists = my_user.wishlist_set.all()
#Because we know that you want to access the wishlist.products
#in order to optimize things (in terms of db queries)
#you can add and .select_related('product')
#e.g, user_wishlists = my_user.wishlist_set.all().select_related('product')
#now follow the wishlist.product foreign key to access the related product for every wishlist
for wishlist in user_wishlists:
product = wishlist.product
print (product.id, product.title, product.url)
Now after you read a little bit more of the documentation
you will notice that your WishList model is in fact an intermediate model for a ManyToMany relation between User and his wished products, then you will know that you can define a M2M field between user and products via WishList like so:
class FbUser(models.Model):
#...
wished_products = models.ManyToManyField(
Product,
through='WishList',
through_fields=('user', 'product')
)
#and now accessing user wished products would be easy as:
user_wished_products = my_user.wished_products.all()
for product in user_wished_products:
print (product.id, product.title, product.url)
This is the first time I am working with DJango.
I am little confused about how my model should look.
Use case is:
There are products.
There are tags.
There are users.
There is a many to many relationship between products and tags.
There is a many to many relationship between users and tags.
I have created two apps right now.
Currently product and tags belong to one app: product
Another app is usrprofile. And I need to add tags to user profile.
Where should Tag reside?
And will tag have reference to both product and user?
Code:
App: Product
class Product(models.Model):
created_at = models.DateTimeField(auto_now_add = True)
updated_at = models.DateTimeField(auto_now = True)
name = models.CharField(max_length=300)
class Tag(models.Model):
created_at = models.DateTimeField(auto_now_add = True)
updated_at = models.DateTimeField(auto_now = True)
name = models.CharField(max_length=300)
display_name = models.CharField(max_length=300)
product = models.ManyToManyField(Product, through='ProductTag')
class ProductTag(models.Model):
product = models.ForeignKey(Product,null=False)
tag = models.ForeignKey(Tag,null=False)
APP: UsrProfile
class UserProfile(models.Model):
created_at = models.DateTimeField(auto_now_add = True)
updated_at = models.DateTimeField(auto_now = True)
email = models.CharField(max_length=300)
Nobody can tell you where your Tag model should best reside in. It's your choice to structure your apps and models. If you want to establish a many-to-many relationship between Tag and UserProfile, you can specify it in the UserProfile model, for instance:
class UserProfile(models.Model):
# ... your other fields ...
tags = models.ManyToManyField('product.Tag')
Note that you have to put the Tag model in a string together with a reference to the product app as shown above. Otherwise, Django will wrongly assume that your Tag model resides in the same app as your UserProfile model. Also, the names of your apps should all be lowercase. Further, it's a good style to give your many-to-many fields plural names, i.e. instead of product in your Tag model use products.
By the way, if you don't need to add additional information to your many-to-many relationships, it's not necessary to define an intermediate model such as ProductTag.