Quering distinct values throught related model - django

I have a simple one-to-many (models.ForeignKey) relationship between two of my model classes:
class TeacherAssignment(models.Model):
# ... some fields
year = models.CharField(max_length=4)
class LessonPlan(models.Model):
teacher_assignment = models.ForeignKey(TeacherAssignment)
# ... other fields
I'd like to query my database to get the set of distinct years of TeacherAssignments related to at least one LessonPlan. I'm able to get this set using Django query API if I ignore the relation to LessonPlan:
class TeacherAssignment(models.Model):
# ... model's fields
def get_years(self):
year_values = self.objects.all().values_list('year').distinct().order_by('-year')
return [yv[0] for yv in year_values if len(yv[0]) == 4]
Unfortunately I don't know how to express the condition that the TeacherAssignment must be related to at least one LessonPlan. Any ideas how I'd be able to write the query?
Thanks in advance.

It is recommended to use the ModelManager for table-level ORM as follows:
class TeacherAssignmentManager(models.Manager):
def get_years(self):
year_values = self.filter(lessonplan__isnull=False).\
values_list('year').distinct().\
order_by('-year')
return [yv[0] for yv in year_values if len(yv[0]) == 4]
Add your custom manager model to TeacherAssignment model class
objects = TeacherAssignmentManager()
Now, you can use TeacherAssignment.objects.get_years() to retrieve all the distinct years that are related to at least one LessonPlan.

TeacherAssignment.objects.filter(lessonplan__isnull=False)
Satisfies your query. Get all TeacherAssignments that have a LessonPlan.
Reverse relationships can be queried this way from the parent model.

Related

retrieve distinct values of manytomanyfield of another manytomanyfield

I have a simple question but multiple google searches left me without a nice solution. Currently I am doing the following:
allowed_categories = self.allowed_view.all().difference(self.not_allowed_view.all())
users = []
for cat in allowed_categories:
for member in cat.members.all():
users.append(member)
return users
I have a ManyToManyField to Objects that also have a ManyToManyField for instances of Users. In the code above, I am trying to get all the users from all those categories and get a list of all Users.
Later I would like the same in a method allowed_to_view(self, user_instance) but that's for later.
How would I achieve this using Django ORM without using nested for-loops?
[edit]
My models are as follows:
class RestrictedView(models.Model):
allowed_view = models.ManyToManyField(Category)
not_allowed_view = models.ManyToManyField(Category)
class Category(models.Model):
name = models.CharField(max_length=30)
members = models.ManyToManyField(User)
So, I've made the following one-liner with only one query towards the database. It took me some time...
users = User.objects.filter(pk__in=self.allowed_view.all().values("users").difference(self.not_allowed_view.all().values("users")))
This gives me a nice queryset with only the users that are in the allowed_view and explicitly not in the not_allowed_view.
Without seeing you database structure / models.py file its hard to say, but you can do a search on member objects like so:
members_queryset = Member.objects.filter(
category = <allowed categories>,
...
)
users += list(members.all())

Double reverse filtering in Django ORM

I am facing one issue while filtering the data .
I am having three models ...
class Product:
size = models.CharField(max_length=200)
class Make(models.Model):
name = models.ForeignKey(Product, related_name='product_set')
class MakeContent(models.Model):
make = models.ForeignKey(Make, related_name='make_set')
published = models.BooleanField()
I can generate a queryset that contains all Makes and each one's related MakeContents where published = True.
Make.objects.filter(make_set__published=True)
I'd like to know if it's possible (without writing SQL directly) for me to generate a queryset that contains all Product and each one's related MakeContents where published = True.
I have tried this
Product.objects.filter(product_set__make_set__published=True)
But it's not working
A subquery can solve the problem.
from django.db.models import Subquery
sub_query = MakeContent.objects.filter(published=True)
Product.objects.filter(
pk__in=Subquery(sub_query.values('make__name__pk'))
)
Use this
Product.objects.filter(make__makecontent__published=True).distinct()
Filtering through ForeignKey works both forward and backwards in Django. You can use the model name in lower case for backward filtering.
https://docs.djangoproject.com/en/3.1/topics/db/queries/#lookups-that-span-relationships
Finally .distinct() is required to remove the multiple values produced due to SQL joins

How to use select_related with Django create()?

class Parent(models.Model):
# some fields
class Child(models.Model):
parent = models.ForeginKey(Parent)
species = models.ForeignKey(Species)
# other fields
I have a function like this:
1. def some_function(unique_id):
2. parent_object = Parent.objects.get(unique_id=unique_id)
3. new_child = Child.objects.create(name='Joe', parent=parent_object)
4. call_some_func(new_child.parent.name, new_child.species.name)
In the line 4, a db query is generated for Species. Is there any way, I can use select_related to prefetch the Species, so as to prevent extra query.
Can it be done while I use .create(). This is just an example , I am using many other fields too and they are querying the DB every time.
The only way I can think is after line 3 using this code:
child_obj = Child.objects.select_related('species').get(id=new_child.id)
The only parameter that create accepts is force_insert which is not related to what you're asking, so it seems it's not possible. Also, noticing that create performs an INSERT ... RETURNING ... statement, I don't think it would be possible anyway because you cannot return columns from other tables.
Possibly the best approach is what you already suggested: do a get() afterwards with the related fields you need.

Dynamic Django Model based on query

Hello folks Im new to Django(I have just the finished the tutorial) but I think i understand the basic concepts of it .Im writing here because Im trying to do something "difficult" for my current experience with django and searching the internet didnt give me a solution .What im trying to do is to create a dynamic model based on the number of entries of another model .To be more exact lets say i got the following model :
class criteria(models.Model):
criteria_text = models.CharField(max_length=200)
monotonicity = models.CharField(max_length=1,choices=(('+','ASCEDING'),('-','DESCENDING')),default='+',verbose_name='Monotonicity')
worst = models.IntegerField(default=0)
best = models.IntegerField(default=0)
What i want to do is create all the criteria models instances i want through the django admin panel and then query for all the creteria_text instances in the database and make a model with an attribute for every criteria_text instance.
So lets say I add the following criteria to the database(these are criteria_text attributes of criteria objects: Color,Weight,Price .
I want to end up with a model like this :
class Alternative(models.Model):
Color = models.IntegerField(default=0)
Weight = models.IntegerField(default=0)
Price = models.IntegerField(default=0)
The thing is that in my application this one has to happen a lot of times so i cannot make model each time someone adds an Alternative based on different criteria .
After searching i found that i can define dynamic models using the following format :
attrs = {
'name': models.CharField(max_length=32),
'__module__': 'myapp.models'
}
Animal = type("Animal", (models.Model,), attrs)
So the question is how can I define "attrs" based on a query that gets all the criteria in the database ?Can i define a relationship of this dynamic model with another model ? Also the models already created should be updated if a user adds more criteria .
Is something like this possible ?
If so please show me the path .
I don't think defining dynamic models is a good solution here (or anywhere, really). Rather, you need a relationship that can have as many items as there are criteria instances.
It might be something like this:
class Alternative(models.Model):
name = models.CharField(...)
choices = models.ManyToManyField("Criteria", through="AlternativeChoice")
class AlternativeChoice(models.Model):
alternative = models.ForeignKey('Alternative')
criteria = models.ForeignKey('Criteria')
value = models.IntegerField(default=0)
The real logic will belong in the form, where you will need to create options for each criteria entry, and validate the AlternativeChoice dependent on the related criteria.

filter with select_related on Django

I have a problem with the usage of select_related feature of Django with the filter operation, here's my problem, I have three classes :
class A:
# various fields here
class B(models.model):
related_A = models.ForeignKey(A)
related_C = models.ForeignKey(C)
attribute1 = models.CharField(..)
# Other attributes
class C(models.model):
# Attributes
What I'm trying to do is, getting class A by filtering on class B on the key related_C according to another parameter attribute1 (from class B).
To illustrate it properly, I have a function get_class_A(self) in my class C
get_class_A(self,param):
classes_B = B.objects.filter(related_C = self,attribute1 = param)
It returns a QuerySet of classes B. What I want to do is to follow the ForeignKey pointing to A in order to transform this QuerySet of B into a list of objects A.
I tried various things such as :
classes_A = B.objects.select_related('A').filter(related_C = self, attribute1 = param)
and some variations but nothing worked. Does anyone knows how to do this ?
Thanks
def get_class_A(self, param):
return A.objects.filter(b__related_c=self, b__attribute1=param).distinct()
What you've described looks a lot like a ManyToMany relationship between A and C. If you declare it as such, and include your extra attributes by specifying B as a through model, Django will create the relationship between A and C for you.
Also, select_related() has nothing to do with filtering results, it's just a tool that can allow you to reduce the number of database queries. From the docs:
This is a performance booster which results in a single more complex query but means later use of foreign-key relationships won’t require database queries.