I'm trying to find a solution that will return the objects with a specific id.
I have these models:
class ModelB(models.Model):
customid = models.CharField(max_length=32)
<-- data -->
class ModelA(models.Model):
b = models.ForeignKey(ModelB, blank=True, null=True, related_name="Bs")
and I have this code in my views:
a = ModelA.objects.filter(ModelB__customid = Bobject_id)
I want to be able to find all of the A objects with a given B object.
Any ideas?
a_objects = ModelA.objects.filter(b__customid=Bobject_id)
Check out the documentation for creating queries that span relationships
Assuming you have an instance of ModelB, the easiest way is to follow the reverse relationship from there:
a_objects = b_object.Bs.all()
This uses the explicit related_name you have set: without that, it would have been b_object.modela_set.all(). Note that your related_name should really be "As", not "Bs", since it refers to the A objects that are related to that B.
Related
I have these models
class Tree(models.Model):
field = models.TextField()
class TaskProgress(models.Model):
base_task = models.ForeignKey(BaseTask, on_delete=models.CASCADE)
tree = models.ForeignKey(Tree, on_delete=models.CASCADE)
class BaseTask(models.Model):
trees=models.ManyToManyField(Tree, through='TaskProgress')
class TaskType1(BaseTask):
child1_field = models.TextField()
class TaskType2(BaseTask):
child2_field = models.TextField()
how to get all taskprogress when related to TaskType2 ,
TaskProgress.objects.filter(???)
I added extra field on BaseTask class
TASK_TYPE =[('I','Irrigation'),('C','Care'),('A','Assessment'),('O','Other')]
class BaseTask(models.Model):
trees=models.ManyToManyField(Tree, through='TaskProgress')
worker = models.ManyToManyField(User)
task_type = models.CharField(max_length=1,choices=TASK_TYPE,null=True)
And the filter will be like this
TaskProgress.objects.filter(base_task__task = "I")
I do not think what you are asking is possible, if the models are designed like described. The base_task ForeignKey is specifically pointing at a BaseTask. Even though TaskType1 and TaskType2 inherit from BaseTask, they have no relation in the database. They only look similar.
Option 1: Look into Generic Relations in Django. Basically it allows you to have a ForeignKey relation with more than one type of model. I would not recommend it though. Generic relations are a mess if you don't know want you are doing.
Option 2: Rethink your layout. Maybe you can move the relation to the two TaskTypes instead and adress them via related_name.
class TaskProgress(models.Model):
# base_task = models.ForeignKey(BaseTask, on_delete=models.CASCADE)
tree = models.ForeignKey(Tree, on_delete=models.CASCADE)
class TaskType1(BaseTask):
task_progress = models.OneToOneField(TaskProgress, related_name='task_type_1'
child1_field = models.TextField()
class TaskType2(BaseTask):
task_progress = models.OneToOneField(TaskProgress, related_name='task_type_2'
child2_field = models.TextField()
This way you create a one-to-one-relation between the TaskProgress and the TaskType. You should be able to query one or the other by checking whether a relation exists, e.g. all TaskProgress instances with a relation to a TaskType1 instance.
# Query all TaskProgress instances, that have a TaskType1
TaskProgress.objects.filter(task_type_1__isnull=False)
Here's a version of my models.py file. I've removed irrelevant fields and the model names are made up, for security reasons:
class FilmStudio(models.Model):
name = models.CharField(max_length=200, unique=True)
class ProductionCompany(models.Model):
name = models.CharField(max_length=200)
film_studio = models.ForeignKey(FilmStudio)
class Meta:
# Ensure that a given combination of ProductionCompany name and FilmStudio object is unique
unique_together = ('name', 'film_studio')
class Film(models.Model):
title = models.CharField(max_length=200)
production_company = models.ForeignKey(ProductionCompany)
class Actor(models.Model):
name = models.CharField(max_length=200)
films = models.ManyToManyField(Film, blank=True)
Although it is not explicitly defined, there is a many-to-many relationship between an Actor and a FilmStudio. This is evidenced by the following call to the Python API:
FilmStudio.objects.filter(productioncompany__film__actor__name='Samuel L. Jackson').distinct()
This returns all of the FilmStudio objects which Samuel L. Jackson is related to, and each one only once. What I'd like is to define extra fields on the relationship between an Actor and a FilmStudio (it doesn't work too well in this example, I know, but it makes sense for my scenario).
Following what is described in Extra fields on many-to-many relationships, I could use an intermediate model to define extra fields on the relationship between a Film and an Actor, for instance.
But this doesn't seem to help me with my problem. I don't want to define the Actor to FilmStudio relationship explicitly, since it's an existing relationship based on other relationships.
Is it possible to define fields on the relationship that I'm describing?
As far as I know, you are not able to do that.
The reason for that is that it is nowhere to store the extra fields of that relationship. If I understand you correctly, these "extra fields" are not implicit in the actor-film or productionstudio-film relationships, so even though you say they are implicit, the extra fields themselves are explicit.
You could try to emulate it by creating an explicit direct relationship whenever it is needed. Then you could simulate the extra fields using the model as an abstraction, but I am not sure if this is what you want. If you opt for this kind of solution you can use default values (in your abstraction) for filling in relationships that don't have a instance yet.
Does that explanation make sense to you?
EDIT:
(I have not double checked that the code works, so be vary)
OK, so you have the original models:
class FilmStudio(models.Model):
name = models.CharField(max_length=200, unique=True)
class ProductionCompany(models.Model):
name = models.CharField(max_length=200)
film_studio = models.ForeignKey(FilmStudio)
class Meta:
# Ensure that a given combination of ProductionCompany name and FilmStudio object is unique
unique_together = ('name', 'film_studio')
class Film(models.Model):
title = models.CharField(max_length=200)
production_company = models.ForeignKey(ProductionCompany)
class Actor(models.Model):
name = models.CharField(max_length=200)
films = models.ManyToManyField(Film, blank=True)
# The "solution" would be:
class ActorProductionComapny(models.Model):
production_company = models.ForeignKey(ProductionCompany, related_name='actors')
actor = models.ForeignKey(Actor, related_name='companies')
# your extra fields here
someproperty = models.CharField(max_length=200)
class Meta:
# let's say one per actor
unique_together = ('production_company', 'actor')
This is going to get messy really quickly
We use a F object like this:
FilmStudio.objects.filter(productioncompany__film__actor__name='Samuel L. Jackson',
productioncompany__film__actor=F('actors__actor'),
actors__someproperty="Plays poker with CEO").distinct()
The tricky part is going to be handling default values (i.e. when there is no value) This would have to be implemented using a custom Manager, but then I am out of my depth.
I will try to explain as well as I can, but it's going to be tricky.
If you want to make a filter on the relationship you may have to do something like this:
def filter_prod(pq_query, someproperty, actor_name):
if someproperty == "Default":
# Great, this means we can ignore the parallel relationship:
return pq_query.filter(productioncompany__film__actor__name=actor_name)
else:
# Here comes the hard part
FilmStudio.objects.filter(productioncompany__film__actor__name=actor_name,
productioncompany__film__actor=F('actors__actor'),
actors__someproperty=someproperty).distinct()
The thing I am trying to illustrate here is that there are two kinds of actor-productioncompany relationships, those with custom field values (non-default), and those without.
Now, you can make a custom getter that looks something like this:
class ProductionCompany(models.Model):
name = models.CharField(max_length=200)
film_studio = models.ForeignKey(FilmStudio)
def get_actors(self):
# This one is not lazy, so be aware
actors = list(self.actors)
# Get a list of actor IDs
actor_ids = [a.actor_id for a in actors]
for actor in Actor.objects.filter(films__production_company_id=self.id):
if actor.id not in actor_ids:
actors.append(ActorProductionComapny(actor=actor, production_company=self)
actor_ids.append(actor.id)
return actors
class Meta:
# Ensure that a given combination of ProductionCompany name and FilmStudio object is unique
unique_together = ('name', 'film_studio')
This should not save the relationship to the database until you call .save() on an instance. You can also add a custom save method that ignores/aports .save() calls where all the values are default. Just remember to check if it is a new instance or not, because you don't want it to cancel a "set back to default" call. You could also make it delete on a "set back to default", but check if you are allowed to do that within .save().
For even more complex queries (mix of default and non-default) you have Q-objects (further down on the page from F objects)
In short, you need to create an extra model to store this extra relational data between Actor and FilmStudio.
class Actor(models.Model):
name = models.CharField(max_length=200)
films = models.ManyToManyField(Film, blank=True)
film_studios = models.ManyToMany(FilmStudio, through='ActorFilmStudio')
class ActorFilmStudio(models.Model):
actor = models.ForeignKey(Actor)
film_studio = models.ForeignKey(FilmStudio)
# define extra data fields here
data1 = models.TextField()
data2 = models.IntegerField()
One way to think about this: the data you're trying to store belongs to an Actor-FilmStudio relation, and is not related in anyway to Film or ProductionCompany.
Your existing ability to retrieve the a set of Actors for a given FilmStudio (or vice-versa) does not necessarily imply you can store relational data belonging to these two models using the models defined in your example.
Keep in mind that each of the models you defined in your example are backed by a table in your database. In the case of Actor.films field, Django creates an extra table to store the many-to-many relationship data.
Since you're looking to store relational data between Actor and FilmStudio, you need to consider where the data will be stored in your database. Can you store the data in the Film model? or the ProductionCompany model?
class Blog(models.Model):
name = models.CharField(max_length=100)
class Entry(models.Model):
blog = models.ForeignKey(Blog)
headline = models.CharField(max_length=255)
Is the following query correct?
a = Blog.objects.get(id__exact=14)
b = Entry.objects.filter(blog = a)
I comprehend that that any of the following methods are more elegant and even recommended.
Entry.objects.filter(blog__id__exact=3) # Explicit form
Entry.objects.filter(blog__id=3) # __exact is implied
Entry.objects.filter(blog__pk=3) # __pk implies __id__exact
In other words, can I pass an object (model instance) as an argument value?
Please also provide some guidance on where can find explicit documentation on this?
Yes, according to the django docs; you can use the instance of a row to do a filter.
Queries over related objects
Alternative:
a = Blog.objects.get(pk=3)
b = a.blog_set.all()
Given the following models
class Category(models.Model):
name = models.CharField(max_length=50)
class Business(models.Model):
name = models.CharField(max_length=50)
category = models.ForeignKey(Category, related_name="businesses")
class Package(models.Model):
business_id = models.ForeignKey(Business)
status = models.CharField(max_length=50)
I have 2 following queries get list of business and categories which the packages are live:
filter_businesses = Business.objects.filter(package__status = 'live')
filter_categories = Category.objects.filter(businesses__package__status = 'live')
Now the questions is, given the related name "businesses" should be equals to category.business_set, why shouldn't the filter in first query be package_set?
Suppose you have two related models: SomeModel and SomeOtherModel, and SomeOtherModel.somemodel is a ForeignKey to SomeModel.
Given any SomeModel instance, the someothermodel_set property is a manager for the related model already filtered. For example:
>>> your_some_model_instance = SomeModel.objects.all()[0]
In this case your_some_model_instance.shomeothermodel_set is equivalent to:
>>> SomeOtherModel.objects.filter(somemodel=your_some_model_instance)
[ update ]
sorry perhaps I didn't explain my questions more clearer, it's complicated to explain... I understand that XX_set and related_name refer to the manager, what I want to ask is in the first query why not use (package_set_status = 'live') given the second working query (businesses_package__status = 'live'), it's confusing because the second query references to the manager(by related_name), but the first query is not...
The filter interface uses the convention relatedmodelname__relatedmodelfield; In your example, related_name was used to give a fancier name to the backreference, but this is not its main purpose; the purpose of the related_name parameter in ForeignKey fields is solving the ambiguity in cases where relatedmodelname clashes with an already existing field at the ForeignKey.
I have 2 models Category and Item. An Item has a reference to a Category.
class Category(models.Model):
name = models.CharField(max_length=32)
class Item(model.Models):
name = models.CharField(max_length=32)
category = models.ForeignKey(Category)
sequence = models.IntegerField()
The sequence field is supposed to capture the sequence of the Item within a category.
My question is:
What Meta Options do i need to set on category and/or item such that when i do:
category.item_set.all()
that I get the Items sorted by their sequence number.
PS: I am now aware of a meta option called ordering_with_respect_to .. but it is still unclear how it works, and also i have legacy data in the sequence columns. I am open to data migration, if the right approach requires that.
What you're looking for is:
class Item(model.Models):
name = models.CharField(max_length=32)
category = models.ForeignKey(Category)
sequence = models.IntegerField()
class Meta:
ordering = ['sequence',]
That will ensure that Items are always ordered by sequence.
category.item_set.all().order_by('sequence')
Kinda late, and the previous answers don't solve my specific question, but they led me to an answer, so I'm gonna throw this in:
I need to sort my prefetch_related objects specifically for only one view, so changing the default ordering is no good (maybe a model_manager would do it, idk). But I found this in the docs.
I have the following models:
class Event(models.Model):
foo = models.CharField(max_length=256)
....
class Session(models.Model):
bar = models.CharField(max_length=256)
event = models.ForeignKey(Event)
start = models.DateTimeField()
....
class Meta:
ordering = ['start']
Now in a particular view, I want to see all the Events, but I want their Sessions in reverse order, i.e., ordering = ['-start']
So I'm doing this in the view's get_queryset():
from django.db.models import Prefetch
session_sort = Session.objects.all().order_by('-start')
prefetch = Prefetch('sessions', queryset=session_sort)
events = Event.objects.all().prefetch_related(prefetch)
Hope it helps somebody!
*BTW, this is just a simplified version, there are a bunch of filters and other prefetch_related parameters in my actual use case.