I'm having problems coming up with a filter in one of my views. I'm creating a site with blog entries, news articles, and reviews. The entries and articles have generic relations with the reviews, because the reviews can tag either of them. What I'm trying to do is to sort the entries/articles based on the sum of the ratings of reviews newer than a certain date.
Here are the simplified models:
class Entry(models.Model):
name = models.CharField(max_length=50)
reviews = generic.GenericRelation(Review)
class Article(models.Model):
name = models.CharField(max_length=50)
reviews = generic.GenericRelation(Review)
class Review(models.Model):
rating = models.IntegerField()
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
target = generic.GenericForeignKey('content_type', 'object_id')
timestamp = models.DateTimeField(auto_now_add=True)
So, given that I needed to find a sum, I tried using annotate and aggregate, but I ran into two problems. The first one is that apparently generic relations and annotations don't work nicely together: https://code.djangoproject.com/ticket/10461. The second issue is that I don't think it's possible to only sum part of the reviews (in this case, with timestamp__gte=datetime.now()). Am I doing this the wrong way?
I also thought about doing this the other way around:
Review.filter(timestamp__gte=datetime.now(), target__in=something).aggregate(Sum('rating'))
But since I'm trying to order the reviews based on this, don't I need to start with Review.something so I can use order_by?
Thanks.
I would highly suggest using Multi-table inheritance instead of generic foreign keys:
class ReviewedItem(models.Model):
item_type = models.IntegerField() # exclude me on admin
def save(self):
self.item_type = self.ITEM_TYPE
super(ReviewedItem, self).save()
class Entry(ReviewedItem):
ITEM_TYPE = 1
name = models.CharField(max_length=50)
class Article(ReviewedItem):
ITEM_TYPE = 2
name = models.CharField(max_length=50)
class Review(models.Model):
item = models.ForeignKey(ReviewedItem)
rating = models.IntegerField()
timestamp = models.DateTimeField(auto_now_add=True)
After doing some research, I found out that the only way to solve my problem was to write custom sql using the "extra" method of QuerySets.
Related
I have the following Model:
class CodeSynonyms(models.Model):
code = models.ForeignKey(Codes, on_delete=models.CASCADE)
websites = models.ManyToManyField(Websites)
synonym = models.Charfield(max_length=10)
The idea is that Websites use the Synonyms for specific Codes. One Website can't have few Synonyms for a Code; various Websites can share the same Synonym for specific Code. The following won't work:
class Meta:
unique together = ('code', 'websites')
" 'unique_together' refers to a ManyToManyField 'websites', but ManyToManyFields are not permitted in 'unique_together' "
Is there a way to solve this keeping the M2M relation? It would be handy to have it
you can use through in ManyToManyFieldand connect your many to many relations through custom intermediate table. then add unique_together there:
class CodeSynonyms(models.Model):
# add through field
websites = models.ManyToManyField(Websites, through='WebsiteCode')
synonym = models.Charfield(max_length=10)
class WebsiteCode(models.Model):
code_synonym = models.ForeignKey(CodeSynonyms, on_delete=models.CASCADE)
website = models.ForeignKey(Websites, on_delete=models.CASCADE)
code = models.ForeignKey(Codes, on_delete=models.CASCADE)
class Meta:
unique together = ('code', 'website')
I am currently learning Django, and I am finding it a bit difficult wrapping my head around the ManyToMany fields. I am using an intermediate model to manage my relationships.
I have three models; Ticket, User, and TicketUserRelation.
I want to be able to query the ticket model, and retrieve both its corresponding user objects and the ticket object. How would I go about doing this?
In Laravel I would do something along the lines of
Ticket::where('id', '1')->with('contributors')
But I can't really figure out how to do this in Django
The models:
class User(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
class Ticket(models.Model):
contributors = models.ManyToManyField(User, through=TicketUserRelation, related_name='tickets')
name = models.CharField(max_length=50)
created_at = models.DateField()
def __str__(self):
return self.name
class TicketUserRelation(models.Model):
id = models.AutoField(primary_key=True, db_column='relation_id')
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
EDIT: I am using an intermediate model so that I can easily add things like join date later.
You don't need the TicketUserRelation model when using Django ORM. You could simply use a ForeignKey in the Ticket model, or use the ManyToManyField already defined, if one ticket can be assigned to multiple users.
class Ticket(models.Model):
# For one user, use ForeignKey
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='tickets')
# For multiple users, use ManyToManyField
contributors = models.ManyToManyField(User, related_name='tickets')
name = models.CharField(max_length=50)
created_at = models.DateField()
def __str__(self):
return self.name
You can then get all tickets for a user u with:
u.tickets.all()
Figured it out myself, using prefetch_related. I was having trouble understanding how prefetch_related works. For those that are confused too, from my understanding it works like this:
Ticket.objects.all().prefetch_related('contributors')
This returns a queryset, something along the lines of this
<QuerySet [<Ticket: Testing ticket one>, <Ticket: Testing ticket two>, <Ticket: Testing ticket three'>, <Ticket: Testing ticket four>]>
When you then access the elements in the queryset, you can then call .contributors on the object, like so:
# Get the queryset
tickets_with_contribs = Ticket.objects.all().prefetch_related('contributors')
# Print the contributors of the first ticket returned
print(tickets_with_contribs[0].contributors)
# Print the contributors of each ticket
for ticket in tickets_with_contribs:
print(ticket.contributors)
Looking back at it this should have been pretty self explanatory, but oh well.
Such a question, I want to create a price comparison site, there are two ideas how to implement a list of prices from different stores.
First via ForeignKey
class Price(models.Model):
price = models.DecimalField()
shop = models.CharField()
class Product(models.Model):
name = models.CharField(max_length=255)
prices = models.ForeignKey()
JSONfield second method
class Product(models.Model):
name = models.CharField(max_length=255)
data = JSONField()
""" Product.objects.create(name='product', data={'price': 999,
'shop': 'Amazon.com'}
def __str__(self):
return self.name
If anyone has experience, what works faster and more reliable if a large number of goods? Thanks for the early ones.
I am building a Quiz app where a user (Content Creator or Author) can create quizzes (choice based questions and their solutions) from a specific domain. These quiz can be attempted by other users (Consumers - not yet implemented).
To allow quiz consumers to be able to search questions based on specific domains of their interest (and to add granularity to the quiz content), I am implementing a tagging system attached to the questions.
Here are my models:
class question(models.Model):
ques_id = models.AutoField(primary_key=True)
ques_text = models.TextField(max_length=1024, blank=False)
ques_author = models.ForeignKey('author')
ques_created = models.DateField(auto_now_add=True)
ques_dscore = models.IntegerField()
ques_bloom = models.CharField(max_length=3)
ques_subject = models.CharField(max_length=3)
ques_type = models.CharField(max_length=1)
ques_flags = models.CharField(max_length=16)
ques_quiz = models.ManyToManyField('quiz')
def __unicode__(self):
return self.ques_text
class choice(models.Model):
choice_id = models.AutoField(primary_key=True)
choice_text = models.CharField(max_length=256, blank=False)
choice_ques = models.ForeignKey('question')
choice_ans = models.BooleanField(default=False)
choice_tags = models.CharField(max_length=32)
def __unicode__(self):
return self.choice_text
class answer(models.Model):
answer_id = models.AutoField(primary_key=True)
answer_text = models.TextField(max_length=1024)
answer_ques = models.ForeignKey('question')
answer_choice = models.ForeignKey('choice')
answer_tags = models.CharField(max_length=128)
class author(models.Model):
user = models.OneToOneField(User)
domain = models.CharField(max_length=16)
def __unicode__(self):
return self.user.username
# a table for storing all the tags
class tags(models.Model):
tags_id = models.AutoField(primary_key=True)
tags_text = models.CharField(max_length=16)
def __unicode__(self):
return self.tags_text
# table that connects tags with question attached to the tag
# from all the research on the web, it can be infered that
# 3NF tagging (Toxi Solution) is the best way to go
# good for inserts but slow on selects
class tagcon(models.Model):
tagcon_id = models.AutoField(primary_key=True)
tagcon_tags = models.ForeignKey('tags')
tagcon_ques = models.ForeignKey('question')
I have currently applied the 3NF tagging Toxi solution. The issue is that a denormalized system would help in faster selects and a 3NF would be faster inserts but slow searches.
I am confused if I should use ManyToMany field type for tags. Could someone enlighten if it would be better to use a ManyToMany field inbuilt in Django or implement the 3NF system as done?
This is already exactly the same as a ManyToManyField. The only difference is that adding the field would give you an explicit accessor from question to tag.
(Note, your models are very odd. There is absolutely no benefit in prefixing every field name with an abbreviated version of the model name; you can only ever access the field via the model anyway, so you would always be doing question.ques_text, which is redundant. And you shouldn't be defining your own PK fields unless you have a very good reason.)
In my opinion I suggest you try these 2 projects
django-tagging.
django-taggit.
Given a queryset for one model, I want to get a queryset of another model that is related by foreign key. Take the Django project docs' weblog schema:
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def __unicode__(self):
return self.name
class Author(models.Model):
name = models.CharField(max_length=50)
email = models.EmailField()
def __unicode__(self):
return self.name
class Entry(models.Model):
blog = models.ForeignKey(Blog)
headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateField()
mod_date = models.DateField()
authors = models.ManyToManyField(Author)
n_comments = models.IntegerField()
n_pingbacks = models.IntegerField()
rating = models.IntegerField()
def __unicode__(self):
return self.headline
Suppose I have an author object, and I want to get every blog that author has written for, as a queryset. I do something like author_blogs = [entry.blog for entry in author.entry_set]. But I'm left with a list in this case, not a queryset. Is there a way I can do this directly with ORM queries, so I can set it up via a custom Entry manager with use_for_related_fields = True and do something like author_blogs = author.entry_set.blogs, and get the benefits of delayed evaluation, etc., of a queryset?
Edited scenario and solution
So, I realized after the fact that the application of my question is slightly different than how I posed it above, for which Daniel Roseman's situation makes a lot of sense. My situation is really more like author.entry_set.manager_method().blogs, where manager_method() returns a queryset of Entry objects. I accepted his answer because it inspired the solution I found which is to do:
author_blogs = Blog.objects.filter(entry__in=author.entry_set.manager_method())
The nice thing is that it only uses one DB query. It's a bit tricky and verbose, so I think it's best to define blogs() as an object method of Author, returning the above.
The trick for this is to remember that if you want a queryset of Blogs, you should start with the Blog model. Then, you can use the double-underscore syntax to follow relations. So:
author_blogs = Blog.objects.filter(entry__authors=author)