I can't figure out the difference between prefetch_related('arg_set') and prefetch_related('arg') .
Sometimes prefetch_related doesn't work when using argument 'arg'even 'arg_set' works.
I've searched through docs.djangoproject.com but, at least, I can't find related document on both pages below.
https://docs.djangoproject.com/en/2.1/ref/models/querysets/ https://docs.djangoproject.com/ja/2.1/ref/contrib/contenttypes/
Could some of you elaborate the difference and when _set is necessary?
And I want to read the official document related to this issue so showing me the reference link is appreciated.
Thank you in advance.
environment:
windows10, python 3.7.2, django 2.1.8, sqlite3, PyCham 2019.1 .
views.py
from django.shortcuts import render
from .models import Article
def index(request):
a = Article.objects.all().select_related('user').prefetch_related('comment_set').order_by('id') # [1]
a = Article.objects.all().select_related('user').prefetch_related('comment').order_by('id') # [2]
return render(request,
'sns/index.html',
{'articles': a})
models.py
from django.db import models
from article_comment_model.settings import AUTH_USER_MODEL
class Article(models.Model):
user = models.ForeignKey(AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='article_user')
title = models.CharField(max_length=100)
text = models.TextField()
class Comment(models.Model):
user = models.ForeignKey(AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='comment_user')
article = models.ForeignKey(Article, on_delete=models.CASCADE)
text = models.TextField()
I want to understand the variety of prefetch_related arguments well.
The argument for callig prefetch_related() would be the name of the relation. In your case this would be a reverse ForeignKey relationship. As you can see in the documentation the name of the reverse relationship will be FOO_set where FOO is the model's lowercase name.
Therefore in your example prefetch_related('comment_set') should be correct. If you would specify a related_name like
article = models.ForeignKey(Article, on_delete=models.CASCADE,
related_name='comments')
the related_name will get used instead of FOO_set, therefore prefetch_related('comments') should be valid in this case.
The name of the lookup to use in prefetch_related depends on the answer to a couple of questions:
Is the relation defined on the model you are querying or on the other side of the relationship?
If the relation is defined on the model you are querying (termed "forward relationship" by the Django docs), then the lookup is simply the field name. If the relation is defined at the other end of the relationship ("backward relationship"), then the lookup depends on a second question:
Have you defined a related_name in the other model where the relationship is defined?
If yes, the lookup is the related_name. If there is no related_name, Django uses a default name, which is modelname_set for x-to-many relationships and modelname for x-to-one relationships (both in lower case).
In practical terms, this means the following in your code:
x-to-many relationships:
# no related names defined, using default manager name
Article.objects.prefetch_related('comment_set')
# using related names
User.objects.prefetch_related('article_user', 'comment_user')
x-to-one relationships:
Article.objects.prefetch_related('user')
Comment.objects.prefetch_related('article', 'user')
Using prefetch_related for x-to-one relationships like in the last two examples above is rare. We mostly use select_related as the latter only creates a join in the original query instead of issuing a separate query. However, as you can read towards the end of its documentation, prefetch_related has a few potential advantages. It can:
fetch a filtered queryset
fetch incomplete models (via only and defer)
perform nested prefetching via the Prefetch object
Related
Considering the following models:
class ManySide(django.db.models.Model):
one_side = models.ForeignKey(
to=OneSide, on_delete=models.PROTECT, related_name="related_one_side"
)
class OneSide(django.db.models:model):
# not containing any field relevant here
def many_side_elements(self):
pass # ?
What should I include in method many_side_elements so that calling it from a OneSide Model instance would list a queryset of ManySide elements?
Official docs imply that given o is a OneSide isntance, o.many_side_set.all() should work but it returns an error in shell.
My current solution is the following:
from django.apps import apps
[...]
def many_side_elements(self):
ManySideModel = apps.get_model('<app_name_here>', 'ManySide')
val = ManySideModel.objects.filter(one_side=self)
But I'm concerned it's ineffective since it requires importing the other Model. Actually it caused a circular dependency error message in my project, hence the get_model usage.
Is there any better way? Or xy_set should work in the first place? Then what am I doing wrong?
If you create the model field with a related name, the related name overrides the _set queryset.
In your case
o.related_one_side.all() should work without the need for another def.
See: https://docs.djangoproject.com/en/4.0/topics/db/queries/#following-relationships-backward
You can override the FOO_set name by setting the related_name parameter in the ForeignKey definition. For example, if the Entry model was altered to blog = ForeignKey(Blog, on_delete=models.CASCADE, related_name='entries'), the above example code would look like this: b.entries.all()
I have a string like this order__product__category__description which is a related expression of my Django's model structure
Now I have a model called Shipment
field = 'order__product__category__description'
Here description is a column name of table/model Category. Here comes the question, just by having this model Shipment and this field string order__product__category__description how do I find these models Order, Product, Category.
My use-case is I need to store all the field_names of Category in a list. Any idea on how to connect the dots? having left with just two details Shipment & that field string.
First thing comes to mind is to split by __ and to come up with a list like this ['order','product','category'] and to iterate the model _meta based on the field name. Any other elegant ways would be appreciated.
If you want to get the related model from the model class (rather than the related instance from a model instance), you can use _meta.get_field() combined with field.related_model to get the related model class:
from django.core.exceptions import FieldDoesNotExist
model = Shipment
lookup = 'order__product__category__description'
for name in lookup.split('__'):
try:
field = model._meta.get_field(name)
except FieldDoesNotExist:
# name is probably a lookup or transform such as __contains
break
if hasattr(field, 'related_model'):
# field is a relation
model = field.related_model
else:
# field is not a relation, any name that follows is
# probably a lookup or transform
break
Then do with model what you want.
I didn't understand well. Anyway hope this you want.
field = 'order__product__category__description'
To get product from Shipment instance
product_var = ".".join(field.split("__")[:2])
Then
from operator import attrgetter
attrgetter(product_var)(shipment_instance)
Also you can get all related as a tuple
attrgetter(".".join(field.split("__")[:1]), ".".join(field.split("__")[:2]), ".".join(field.split("__")[:3]), ".".join(field.split("__")[:4]))(shipment_instance)
Hope this helps.
Assuming you have split the string it into a list, as you said to these:
models = ['order', 'product', 'category']
First get the app label (a string) that each model refers to:
from django.contrib.contenttypes.models import ContentType
app_labels = []
for model in models:
app_labels.append(ContentType.objects.filter(model=model).values('app_label').first().get('app_label'))
Now you have both model names and app names. So:
from django.apps import apps
model_classes = []
for app, model in zip(app_labels, models):
model_classes.append(apps.get_model(app, model))
Finally, get each model fields with the _meta attribute like you already know:
for model in model_classes:
print(model._meta.get_fields())
I have following three models (simplified):
from django.db import models
class UserProfile(models.Model):
pass
class Gallery(models.Model):
user_profile = models.ForeignKey('UserProfile')
class Photo(models.Model):
gallery = models.ForeignKey('Gallery')
On profile detail I want to show all galleries that have at least one photo.
I tried to go this way:
Gallery.objects.all().filter(user_profile__pk=x). \
annotate(count_photos=Count('photo_set')).filter(count_photos__gt=0)
But this leads to an error:
FieldError: Cannot resolve keyword 'photo_set' into field. Choices are: id, user_profile
I understand, that Gallery.objects.all() is Queryset, so this is probably not possible. Solution could be to start from Photo:
Photo.objects.all().filter(gallery__user_profile__pk=x)
But I need to iterate in template over galleries and not over photos.
You should use
Gallery.objects.filter(user_profile__id=x)
.annotate(count_photos=Count('photo__id'))
.filter(count_photos__gt=0)
In annotate, it is not X_set but just X.
This would also work,, Shorthand for above:
Gallery.objects.filter(user_profile__id=x)
.annotate(count_photos=Count('photo'))
.filter(count_photos__gt=0)
One more way to Make this query:
Gallery.objects.filter(user_profile__id=x, photo__isnull=False).distinct()
I have two models designated for tracking what users have upvoted an Article instance (in another app, in this case articlescraper).
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User)
articles_upvoted = models.ManyToManyField('useraccounts.UpvotedArticle',
null=True,
blank=True)
class UpvotedArticle(models.Model):
article = models.ForeignKey('articlescraper.Article')
user = models.ForeignKey(User)
In a Django shell, I've tried to get a list of articles by interacting with UserProfile:
a = UserProfile.objects.get(pk=1)
a.articles_upvoted.all()
Which returns:
[]
However, then I went a little further:
b = UpvotedArticle.objects.filter(user=User.objects.get(pk=1))
b
Which returns:
[<UpvotedArticle: Arch Linux Lexmark S305 Drivers>, <UpvotedArticle: Structure of a Haystack project>]
Which is the expected behavior, and is mirrored in the Django admin in both UserProfile and UpvotedArticle categories.
I don't understand, however, why attempting to get a list of articles can't be done the way I initially tried to using a.articles_upvoted.all() if the two models are linked.
Because these aren't the same relationship. By defining a ForeignKey on one side, and a ManyToMany on the other, you've given the database two separate places to store information about article upvoting.
You should remove the ManyToManyField on UserProfile, and just use the automatic reverse relationship:
a = UserProfile.objects.get(pk=1)
a.upvotedarticle_set.all()
Alternatively, you could recognize UpvotedArticle as the "through" table of the ManyToMany relationship, and mark it as such explicitly in the definition of articles_upvoted - note though that the relationship should be with articlescraper.Article, not UpvotedArticle:
article_upvoted = models.ManyToManyField(articlescraper.Article, null=True,
blank=True, through=UpvotedArticle)
Although since you're not adding any extra data on that relationship, which is the usual reason for defining an explicit through table, you may want to drop it completely and just rely on the automatic one that Django will create.
I'm coming from a Rails background, and am having a bit of trouble making use of the "Association Methods" provided in Django. I have two models (which have been simplified for the sake of brevity), like so:
class User(models.Model):
username = models.CharField(max_length=100, unique=True)
companies = models.ManyToManyField('Company', blank=True)
class Company(models.Model):
name = models.CharField(max_length=255)
According to the Django documentation:
"It doesn't matter which model has the ManyToManyField, but you should only put it in one of the models -- not both.".
So I understand that if I have an instance of a User, called user, I can do:
user.companies
My question is how do I do the reverse? How do I get all users that belong to a Company instance, let's say Company:
company.users # This doesn't work!
What's the convention to do this? The documentation that I've read doesn't really cover this. I need the association to work both ways, so I can't simply move it from one model to the other.
company.user_set.all()
will return a QuerySet of User objects that belong to a particular company. By default you use modelname_set to reverse the relationship, but you can override this be providing a related_name as a parameter when defining the model, i.e.
class User(models.Model):
companies = models.ManyToManyField(Company, ..., related_name="users")
> company.users.all()
here is the relevant documentation