I have a question about Django model references.
I want to add more constraints to a ForeignKey or ManytoManyField like this:
question_id = models.ManyToManyField(Question(open==True))
Or put another similar constraint:
Actually I don't want to show a question that is not open as a selection( heightened in Answer form like the image above),
of course, I have done it by some query but does Django has any built-in support for it? I have tried some other way but it didn't work. Thanks!
class Answer(models.Model):
"""Give answers"""
answer = models.TextField()
question_id = models.ManyToManyField(Question(open==True))
upVote = models.IntegerField(default=0)
downVote = models.IntegerField(default=0)
def __str__(self):
"""return string """
return self.answer
While you still can, change your model relations. What you have now is that an answer can be linked to multiple questions. However, answer contains up and down vote.
So I can create two questions:
Is 1 + 1 two?
yes
no
Is 1 + 1 three?
yes
no
I can link the answers "yes" and "no" to both questions, which can be convenient with a good interface and is normalized. But they will share the up/down votes. Instead, answer should have a foreign key to question, because an answer can only be linked to one question at a time, even if the answer text is identical to prevent sharing of the up and down votes.
Secondly, we generally don't name fields question_id, but question:
From an object relation perspective, you relate an answer to a question not to a question id. (Under the hood, question_id is created to allow faster lookups and to serve as field name in the underlying database table).
On to your actual problem: you want to limit the available choices, which is what limit_choices_to does for you. So you would end up with this:
class Answer(models.Model):
"""Give answers"""
answer = models.TextField()
question = models.ForeignKey(
Question, on_delete=models.CASCADE, related_name='answers',
limit_choices_to={'open': True},
)
upVote = models.IntegerField(default=0)
downVote = models.IntegerField(default=0)
def __str__(self):
"""return string """
return self.answer
# Serializer
class AnswerSerializer(serializers.ModelSerializer)
class Meta:
model = Answer
fields = ('answer', 'upVote', 'downVote', 'question_id')
As you can see, your serializer can reference the magic field question_id.
Related
I want a method on a django model that returns the count of members in a foreignkey. I'd prefer it to be a class method (and not annotation) because I want to serialize it and send it via javascript without involving templates.
Based on previous answers to similar questions, I believed the following sort of structure would work (some attributes deleted for the sake of simplicity):
class Post(models.Model):
likedby = models.ForeignKey('User', on_delete=models.CASCADE, null=True, blank=True, related_name='liked')
def likes(self):
return self.likedby_set.count()
But when I run this I get AttributeError: 'Post' object has no attribute 'likedby_set'. Anyone know why this doesn't work?
Edit: Thanks for the help with all this. I'm now thinking: would it make more sense to restructure the models by adding a Like class like so?
class Like(models.Model):
liker = models.ForeignKey('User', on_delete=models.CASCADE, related_name='likes')
post = models.ForeignKey('Post', on_delete=models.CASCADE, related_name='likes')
Then perhaps in the Post class I could return likes_set.count() (assuming that's valid)
Edit 2: In case useful to people who may look at this post in the future, the above solution won't work because likes isn't a queryset; it's just a related name pointing to the people who liked the post. In order to gather those people up correctly into a queryset and make the count, I did the following in my serialization defined within the model: likes = self.likes.all().count(), which seems to work.
I am having trouble deciding how to structure my models for a particular data structure.
The models I have would be Posts, Groups, Users.
I want the Post model that can be posted from a groups page or user page and potentially more, like an events page.
Posts would contain fields for text, images(fk), user, view count, rating score (from -- a reference to where ever it was posted from like user or group page, though I am unsure how to make this connection yet)
I thought about using a Generic Foreign Key to assign a field to different models but read articles suggesting to avoid it. I tried the suggested models, but I wasn't unsure if they were the right approach for what I required.
At the moment I went with Alternative 4 - multi-table inheritance
class Group(models.Model):
name = models.CharField(max_length=64)
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='_groups')
members = models.ManyToManyField(
settings.AUTH_USER_MODEL)
def __str__(self):
return f'{self.name} -- {self.created_by}'
def save(self, *args, **kwargs):
# https://stackoverflow.com/a/35647389/1294405
created = self._state.adding
super(Group, self).save(*args, **kwargs)
if created:
if not self.members.filter(pk=self.created_by.pk).exists():
self.members.add(self.created_by)
class Post(models.Model):
content = models.TextField(blank=True, default='')
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="%(app_label)s_%(class)s_posts",
related_query_name="%(app_label)s_%(class)ss")
# class Meta:
# abstract = True
def __str__(self):
return f'{self.content} -- {self.created_by}'
class PostImage(models.Model):
image = models.ImageField(upload_to=unique_upload)
post = models.ForeignKey(
Post, related_name='images', on_delete=models.CASCADE)
def __str__(self):
return '{}'.format(self.image.name)
class UserPost(models.Model):
post = models.OneToOneField(
Post, null=True, blank=True, related_name='_uPost', on_delete=models.CASCADE)
class GroupPost(models.Model):
post = models.OneToOneField(
Post, null=True, blank=True, related_name='_gPost', on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
To do some specific filters ex:
Filter specific group post
Post.objects.filter(_gPost__group=group)
Filter specific user post
Post.objects.filter(created_by=user) # exclude groups with _gPost__isnull=False
Create post to user/group
p = Post.objects.create(...)
up = UserPost.objects.create(post=p)
gp = GroupPost.objects.create(post=p)
Really I am wondering if this is a sensible approach. The current way of a filter and creating feel odd. So only thing making me hesitant on this approach is just how it looks.
So, is Generic ForeignKey the place to use here or the current multi-table approach. I tried going with inheritance with abstract = True and that was unable to work as I need a foreign key to base post model. Even with no abstract, I got the foreign key reference, but filter became frustrating.
Edit:
So far only weird issues(but not really) are when filtering I have to be explicit to exclude some field to get what I want, using only .filter(created_by=...) only would get all other intermediate tables.
Filter post excluding all other tablets would requirePost.objects.filter(_uPost__isnull=True, _gPost__isnull=True, _**__isnull=True) which could end up being tedious.
I think your approach is sensible and that's probably how I would structure it.
Another approach would be to move the Group and Event foreignkeys into the Post model and let them be NULL/None if the Post wasn't posted to a group or event. That improves performance a bit and makes the filters a bit more sensible, but I would avoid that approach if you think Posts can be added to many other models in the future (as you'd have to keep adding more and more foreignkeys).
At the moment I will stick with my current pattern.
Some extra reading for anyone interested.
https://www.slideshare.net/billkarwin/sql-antipatterns-strike-back/32-Polymorphic_Associations_Of_course_some
I'm creating a "Polls" like application. In this application there will be questions and every question will have single or multiple answers choices. The answer choice can be Text, Image, or Video.
I have made one model called Question and different models for different choice types:
class Question(models.Model):
question_text = models.TextField()
category = models.ForeignKey(QuestionCategory)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class ChoiceText(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice = models.CharField(max_length=255)
class ChoiceImage(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice = models.ImageField()
Is this a good way to design the models, or is there a better way to solve this problem ?
It doesn't look so intuitive in the admin side too, any suggestions on the admin side ? I looked at https://docs.djangoproject.com/en/2.1/intro/tutorial07/ but I'm still confused about combining the different choices into a better user-friendly way.
I suggest you to go with same model (Choice). You can have a field named choice_type which selects the type of choice ('image','text','video').
class Choice(models.Model):
answer_type_choices = (
('1','Text'),
('2','Image'),
('3','Video'),
)
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_type = model.CharField(max_length=1,choices = answer_type_choices)
choice = models.CharField(max_length=255)
choice_media = models.FileField(upload_to='uploads/')
And while adding the data from admin or front you can have form with some customization that has validation rules in clean method ( require fields based on choice type, image field validation, video file validation ) based on choice_type.
This is probably the best way to do it, assuming a question could have multiple answers of the same type, e.g. one question might have 4 text choices and 2 image choices.
Depending on how you plan on using these models, you may want to create a model class called Choice and have the other models subclass from it.
Django's admin form is highly customizable. Since you haven't given us any specifics, feel free to explore the Django admin docs.
I do not want to apply permanent ordering with the default ordering in Meta. I would rather update it at run time and than access it from templates. Is there a more elegant way than the following?
def get_object(self, queryset=None):
question = super().get_object(queryset)
question.choice_set.ordered = question.choice_set.order_by('-votes')
return question
Because this will not update if the underlying objects changed.
Why don't you use the order_by method of the QuerySet?
Assuming you have a modell Question, which has some related model, let's call it Answer:
class Answer(models.Model):
content = models.CharField()
votes = models.IntegerField()
class Question(models.Model):
content = models.CharField()
answer = models.ForeignKey(Answer, related_name="questions")
Then you can order your retrieved results like this:
q = Question.objects.order_by('-answer__votes')
This is everything done with Django tools, no need to create anything extra.
I have a Q&A type of site built in Django with the following models:
class Question(models.Model):
title = models.CharField(max_length=70)
details = models.TextField()
class Answer(models.Model):
question_id = IntegerField()
details = models.TextField()
I need to display a specific question together with its answers. Normally I'd need 2 queries to do that:
Question.objects.get(id=1)
Answer.objects.get(question_id=1)[:10]
I'm hoping to retrieve everything using one query. In MySQL it'd be:
SELECT *
FROM Question JOIN Answer ON Question.id=Answer.question_id
WHERE Question.id=1
LIMIT 10
Is there anyway I could do this through Django's ORM?
Would extra() help in this case?
This is exactly what select_related() does. The only gotcha is that
you have to start with the Answer model, rather than Question, but the
result is the same:
answers = Answer.objects.filter(question_id=1).select_related()
Now each answer object has a pre-fetched 'question' attribute, and
accessing it won't hit the db again.
Consider using models.ForeignKey(Question) instead of question_id = IntegerField().
This is the optimal (more relational) way to express the relationship between Questions and Answers you are trying to portray.
This way you can simply call Answers.objects.filter(question_id=<id>) and get exactly what you're looking for.
class Question(models.Model):
title = models.CharField(max_length=70)
details = models.TextField()
class Answer(models.Model):
question = models.ForeignKey('Question')
details = models.TextField()
id = <whatever_id>
answers = Question.objects.get(id=id).answer_set.all()
#Consider A Foreign Key Relationship Between Books And Publisher
class Publisher(models.Model):
name = models.CharField(max_length=100)
eclass Book(models.Model):
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
#Fetch Publisher Name For A Book
book = Book.objects.select_related('publisher').get(id=1)
book.publisher.name
#Fetch books which have specific publisher
publisher = Publisher.objects.prefetch_related('book_set').get(id=1)
books = publisher.book_set.all()
for more
https://kwikl3arn.com/django/JOINS