I have such models:
class Genre(models.Model):
name = models.TextField(null=True, blank=True)
min_age = models.IntegerField(default=0)
max_age = models.IntegerField(default=0)
objects = GenreManager()
class Meta:
get_latest_by = 'id'
class Book(models.Model):
name = models.TextField(null=True, blank=True)
genres = models.ManyToManyField(Genre, related_name='genres')
suggested_age = models.IntegerField(default=0)
objects = BookManager()
and I want to query it in such way: there can be duplicates (when min/max age changes, new object will be saved to db), but I want to get the latest one, so I come up with:
class GenreManager(models.Manager):
def get_latest_genre_obj(self, genre):
return self.get_queryset().filter(name=genre).latest()
to be able to retrieve one. Now, I want to get books, using Genre object from above - I need min and max age values in my query. I've tried something like this:
class BookManager(models.Manager):
def get_books_from_genre(self, genre):
genre = Genre.objects.get_latest_genre_obj(genre)
return self.get_queryset().annotate(actual_genre=genre).filter(actual_genre__min_age__gte=F('suggested_age'), actual_genre__max_age__lte=F('suggested_age'))
But it looks like I cannot annotate object to other object:
AttributeError: 'Genre' object has no attribute 'resolve_expression'
So, how to query it like I want to? I thought about Subquery() as I thought it can help me, but it's Django 1.11 feature andI'm using 1.9 version. To sum up what I want to achieve: from genres, retrieve newest (with highest id) object with particular name and use it's fields while querying for books - so I need to have access to it's fields.
I think you need to remove annotate from the code, and use it like this:
If you want Books for a specific genre:
class BookManager(models.Manager):
def get_books_from_genre(self, genre):
genre_object = Genre.objects.get_latest_genre_obj(genre)
return self.get_queryset().filter(genres=genre_object)
Now if you want to have books from multiple genre with same name, then you can try like this:
class BookManager(models.Manager):
def get_books_from_genre(self, genre):
return self.get_queryset().filter(genres__name=genre, genres__min_age__gte=F('suggested_age'), genres__max_age__lte=F('suggested_age'))
Related
I really don't understand all the ways to build the right query.
I have the following models in the code i'm working on. I can't change models.
models/FollowUp:
class FollowUp(BaseModel):
name = models.CharField(max_length=256)
questions = models.ManyToManyField(Question, blank=True, )
models/Survey:
class Survey(BaseModel):
name = models.CharField(max_length=256)
followup = models.ManyToManyField(
FollowUp, blank=True, help_text='questionnaires')
user = models.ManyToManyField(User, blank=True, through='SurveyStatus')
models/SurveyStatus:
class SurveyStatus(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
survey = models.ForeignKey(Survey, on_delete=models.CASCADE)
survey_status = models.CharField(max_length=10,
blank=True,
null=True,
choices=STATUS_SURVEY_CHOICES,
)
models/UserSurvey:
class UserSurvey(BaseModel):
user = models.ForeignKey(User, null=True, blank=True,
on_delete=models.DO_NOTHING)
followups = models.ManyToManyField(FollowUp, blank=True)
surveys = models.ManyToManyField(Survey, blank=True)
questions = models.ManyToManyField(Question, blank=True)
#classmethod
def create(cls, user_id):
user = User.objects.filter(pk=user_id).first()
cu_quest = cls(user=user)
cu_quest.save()
cu_quest._get_all_active_surveys
cu_quest._get_all_followups()
cu_quest._get_all_questions()
return cu_quest
def _get_all_questions(self):
[[self.questions.add(ques) for ques in qstnr.questions.all()]
for qstnr in self.followups.all()]
return
def _get_all_followups(self):
queryset = FollowUp.objects.filter(survey__user=self.user).filter(survey__user__surveystatus_survey_status='active')
# queryset = self._get_all_active_surveys()
[self.followups.add(quest) for quest in queryset]
return
#property
def _get_all_active_surveys(self):
queryset = Survey.objects.filter(user=self.user,
surveystatus__survey_status='active')
[self.surveys.add(quest) for quest in queryset]
return
Now my questions:
my view sends to the create of the UserSurvey model in order to create a questionary.
I need to get all the questions of the followup of the surveys with a survey_status = 'active' for the user (the one who clicks on a button)...
I tried several things:
I wrote the _get_all_active_surveys() function and there I get all the surveys that are with a survey_status = 'active' and then the _get_all_followups() function needs to call it to use the result to build its own one. I have an issue telling me that
a list is not a callable object.
I tried to write directly the right query in _get_all_followups() with
queryset = FollowUp.objects.filter(survey__user=self.user).filter(survey__user__surveystatus_survey_status='active')
but I don't succeed to manage all the M2M relationships. I wrote the query above but issue also
Related Field got invalid lookup: surveystatus_survey_status
i read that a related_name can help to build reverse query but i don't understand why?
it's the first time i see return empty and what it needs to return above. Why this notation?
If you have clear explanations (more than the doc) I will very appreciate.
thanks
Quite a few things to answer here, I've put them into a list:
Your _get_all_active_surveys has the #property decorator but neither of the other two methods do? It isn't actually a property so I would remove it.
You are using a list comprehension to add your queryset objects to the m2m field, this is unnecessary as you don't actually want a list object and can be rewritten as e.g. self.surveys.add(*queryset)
You can comma-separate filter expressions as .filter(expression1, expression2) rather than .filter(expression1).filter(expression2).
You are missing an underscore in surveystatus_survey_status it should be surveystatus__survey_status.
Related name is just another way of reverse-accessing relationships, it doesn't actually change how the relationship exists - by default Django will do something like ModelA.modelb_set.all() - you can do reverse_name="my_model_bs" and then ModelA.my_model_bs.all()
Given the following model that stores the user's wish list for reading books:
class ReadingList(models.Model):
user_id = models.ForeignKey(UserInfo, on_delete=models.DO_NOTHING, null=False, blank=False, default=None, db_column='user_id')
book= models.CharField(max_length=255, blank=False)
creation_time = models.DateTimeField(blank=True)
class Meta:
unique_together = (('user_id', book),)
I want to create a model that helps in tracking the time spent in the reading the book on different days which looks something like this:
class ReadingTracker(models.Model):
user_id = models.ForeignKey(ReadingList, on_delete=models.DO_NOTHING, related_name='user', blank=False, db_column='user_id')
book= models.ForeignKey(ReadingList, on_delete=models.DO_NOTHING, related_name='book-to-read', blank=False, db_column='book')
time = models.DateTimeField(blank=True)
time_spent = models.floatfield()
On the client-side (corresponding to ReadingTracker) for both the fields user_id and book
I see that ReadingList object (1), ReadingList object (2), ... are listed. But, this is not working as expected.
What I want to achieve are the following:
For user_id field I want to see the something like dummy_uid1, dummy_uid2, ... to be listed.
Consider dummy_uid1 wants to read book1 and book2 whereas dummy_uid2 wants to read book1 and book3.
When dummy_uid1 is selected as user_id, I want only book1 and book2 to be listed for selection.
How do I define the model in django rest framework to achieve this?
Any suggestions related to the above would be much appreciated and thank you in advance.
There are two parts to this question:
If you want to see a different value than ReadingList object (1) then you need to define the __str__ value of your model, you can do this like so:
class ReadingList(models.Model):
...
def __str__(self):
return f'{self.user_id}' # return whatever string you want to display
If you want to just display the books for a particular user then you can use a filter() (see the Django documentation):
reading_list = ReadingList.objects.get(...)
ReadingTracker.objects.filter(user_id=reading_list)
However, I would add that you have a user_id on your ReadingList object which does seem to connect to a User model, but your user_id on ReadingTracker is a ForeignKey relation to ReadingList, which is confusing. I would suggest renaming the field or actually making it link to the User model (though this is unnecessary as you can still filter by User through the ReadingList model).
I've got a complicated relationship between my Django models and I'm trying to get django-import-export to play nicely.
class Person(models.Model):
name = models.CharField(max_length=64)
class Team(models.Model):
rep = models.ManyToManyField(Person, related_name="rep")
def get_reps(self):
return "/".join(sorted([p.name for p in self.reps.all()]))
class Account(models.Model):
tid = models.IntegerField("Territory ID", primary_key=True)
name = models.CharField("Territory Name", max_length=64)
sales_team = models.ForeignKey(Team, related_name="sales_team")
I'm trying to export (and hopefully later import) the territories with the names of the reps as rendered by the get_reps method.
class TerritoryResource(resources.ModelResource):
tid = fields.Field(attribute='tid', column_name="Territory ID")
name = fields.Field(attribute='name', column_name="Territory Name")
sales_team = fields.Field(
column_name="Sales Team",
widget=widgets.ForeignKeyWidget(Team, "get_reps")
)
The export is giving me a blank field. If I don't use the widget I get the Team ID as I'd expect.
Is it possible to get my custom name in the export?
I didn't include the standard __str__ methods in the sample code, because I didn't think they were important, but I did have in my Team class definition:
def __str__(self):
return self.get_reps()
This means, had I read the documentation with a little more creativity, I would have figured out how to do this. It's deceptively simple:
class TerritoryResource(resources.ModelResource):
...
def dehydrate_sales_team(self, territory):
return str(territory.sales_team)
I could also use return territory.sales_team.get_reps() to get the same results.
My Django models look like this:
class User(models.Model):
userid = models.CharField(max_length=26,unique=True)
#some more fields that are currently not relevant
class Followers(models.Model):
user = models.ForeignKey('User',related_name='usr')
coins = models.IntegerField()
followers = models.CharField(max_length=26, null=True, blank=True)
I would now like to make a filter query in my Followers table selecting every entry where users have ID x and followers have ID y (I expect to get one result from the query).
To visualize what I have tried and know won't work is this:
queryfilter = Followers.object.filter(followers=fid, user=uid)
and this:
queryfilter = Followers.object.filter(followers=fid, user__userid=uid)
In the end I would like to access the coins:
c = queryfilter.coins
It may be possible that I cannot do it with one single query and need two, since I am trying to do a filter query with two tables involved.
Firstly, I have modified your 'Followers' model (for naming convention).
models.py
class Follower(models.Model):
user = models.ForeignKey('User', related_name='followers')
coins = models.IntegerField()
key = models.CharField(max_length=26, null=True, blank=True)
Your queryset should be ..
views.py
#coins
coins = Follower.objects.filter(key=fid, user__userid=uid).get().coins
I have the models:
class Category(models.Model):
description = models.CharField(unique=True, max_length=200)
class Car(models.Model):
categorys = models.ManyToManyField(Category)
Constantly new cars are added.
I'd like to get the last 20 categories that had a car of her type added.
I got to set up a filter, but when I got to order by I could not get it anymore.
Since I can not close any logic I will not put what I have tried here.
Updating
I add 3 cars:
Car 1 have 2 categories (cat1 and cat2)
Car 2 have 3 categories (cat2, cat20, cat3)
Car 3 have 2 categories (cat4, cat1, cat90)
I need to get:
cat1, cat2, cat3, cat4, cat20, cat90 and others...
So you have this models:
class Category(models.Model):
description = models.CharField(unique=True, max_length=200)
class Car(models.Model):
categorys = models.ManyToManyField(Category)
According to this phrase:
I'd like to get the last 20 categories that had a car of her type
added.
at first we have to find the car:
car = Car.objects.get("""your parameters""")
Then we have to find categories:
category = car.category_set.all()[:20]
Try this
UPD
When you get all Cars and Category that you need, you can get to any of category object.
categories = Cars.objects.filter("""your filter""").select_related('categorys')
for category in categories.categorys_set():
print(category.description)
I use extra() function to solve the problem.
See code blow:
class School(models.Model):
...
tag = models.ManyToManyField(Tag)
def tags(self):
meta = self.tag.through._meta
db_table = meta.db_table
primary_key = meta.pk.name
order_by = [db_table + '.' + primary_key]
return list(self.tag.extra(order_by=order_by).values_list('name', flat=True))
By adding order by to sql statement to make results order by pk of through (if not specified django will create a table for you automaticly) table.
Hope this will be helpful.