Django backwards relation - python

I am setting up webservices for an application and I have the following models:
class Parent(models.Model):
...
class Child(models.Model):
parent = models.ForeignKey(Course)
...
The relation is One to Many (1 Parent, many Children)
Now, I would like to get all the Parent objects with its particular Child and send it as a JSON Request.
Is it possible to do so without having to first get all the "Childs" and iterate through them looking for the ones related to the particular parent?
I think that would be extremely inefficient for really large databases, plus the "Childs" won't be repeated in other "Parents"
Thank you very much

Every relationship in Django automatically gets its reverse relation added to the model. In the case of a ForeignKey or ManyToManyField that relation contains several objects. In that case, the default attribute name is set to <model>_set, so in this case child_set. This is a manager and can be used as such, so e.g. to iterate over all children:
for child in parent.child_set.all():
do_something()
You can also specify the attribute name used for the reverse relation using the related_name attribute:
class Child(models.Model):
parent = models.ForeignKey(Parent, related_name='children')
for child in parent.children.filter(some_field=True):
do_something()
Read more in the documentation on following relations backwards and how are backward relationships possible.

Why would you need to iterate? Even if Django didn't provide you with a special backwards syntax, you could always do this:
Child.objects.filter(parent=my_parent)
but as a cursory Google for the title of your question would have shown, there is a special syntax for backwards relations:
my_parent.child_set.all()

Yes, in django you can use:
parentInstance.child_set.all()
where parentInstance is one particular parent in your Parent database. That will return all of the Child objects associated with it in an efficient manner. To make it a JSON response, you can try something like this:
import json
from django.http import HttpResponse
response_data = {}
response_data[str(parentInstance)] = parentInstance.child_set.all()
return HttpResponse(json.dumps(response_data), content_type="application/json"
adopted from here.

Related

Django .values_list() alternative that returns a QuerySet for a ForeignKey field's model?

I'm looking for a clean way to convert one type of QuerySet to another based on a models ForeignKey field, so basically something like a .values_list('my_fk', flat=True) but returning a proper QuerySet instead of a values_list() variant.
For example:
class Parent(models.Model):
child = models.ForeignKey(Child)
...
children_qs = Parent.objects.filter(...).theMagicMethod('child')
Here children_qs should now be a queryset for all Child instances used in the earlier query, instead of a queryset returning Parent instance.
You can sort of do this with a custom queryset and an __in lookup but it feels a bit smelly:
class ParentQuerySet(models.QuerySet):
...
def children(self):
return Child.objects.filter(id__in=self.values_list('child_id', flat=True))
This takes all the child_id FK's from the records in the Parent's queryset and re-query Child directly. When I inspect the SQL it does a sub query, and I'm not sure if this is optimal or has some odd side effects. It does look like the ordering from the original Parent query is gone, and so are duplicates.
Does anyone got something better then this?
note: I'm aware I could query directly via Child and filter the Parent's fields using the reverse lookup, but that doesn't support everything you can do on the main model.
Try this, it will return query_set of Child class
parent_primary_keys = Parent.objects.filter(...).values_list('pk',flat=True)
children_qs = Child.objects.filter(id__in=parent_primary_keys)
It sounds like you can leverage the Django's function prefetch_related. Check out this answer and django's documentation

Django, How to change "blank" attribute of field from parent model?

I got two models, for example:
Parent(models.Model):
mytext= models.Chafield(max_lenght=250, blank=True)
Child(Parent):
mytext_comment=models.Chafield(max_lenght=250)
But in child I want mytext to be obligatory.
Do it will be sufficient to invoke mytext.blank=False in child __init__ ?
Caution this are not abstract methods because I want to be able to use Manager on Parent (Parent.objects.all() for example)
I don't think its possible. From Django Documentation:
This restriction only applies to attributes which are Field instances.
Normal Python attributes can be overridden if you wish. It also only
applies to the name of the attribute as Python sees it: if you are
manually specifying the database column name, you can have the same
column name appearing in both a child and an ancestor model for
multi-table inheritance (they are columns in two different database
tables).
PS: I tried to like you suggested, but I get error like unicode object has no attribute blank
Hmm you can try this solution:
Parent(models.Model):
mytext= models.Chafield(max_lenght=250, blank=True)
Child(Parent):
mytext_comment=models.Chafield(max_lenght=250)
Child._meta.get_field('mytext').blank = True
Can you please let me know if it works ?
As the discussion goes on I think the correct answer is:
You don't do it on model level. I should do this kind of validation on form level not in a model. Best places are: form fields parameters or form clean method

Django creating multiple tables/model classes from same base class with factory function

I have been trying to figure out the best way to automate creating multiple SQL tables based on separate but identical models, all based on the same base class. I'm basically creating pseudo message boards or walls with different Groups, and I wanted each Group to have its own db_table of Posts, each Post containing the user id, timestamp, etc.
My first thought was to have one base class of Posts and just include a field for Group name, but I thought this would be bad practice. My rationale was that one table containing every Post for all Groups would get really big (in theory anyway) and slow down filtering, and also that the extra field for group name would in the long run be a waste of memory when I could have separate tables per group and skip this field.
I've also considered using a ForeignKey with a Many-to-One relationship, but as far as I can tell this has the same drawbacks. Am I wrong to think that? Or are these size concerns not really an issue?
So my next idea was to make Posts an abstract class, and then create subclasses based on each Group. This is ultimately what I did. However, I found myself having to copy and paste the code over and over and change the class name each time. This felt very unPythonic to me. It was something like:
class Posts(models.Model):
timestamp = models.DateTimeField(auto_now_add=True, unique=False)
user_id = ...
#etc.
#
class Meta:
abstract = True
class GroupA(Posts):
class Meta(Posts.Meta):
db_table = 'groupa_board'
class GroupB(Posts):
class Meta(Posts.Meta):
db_table = 'groupb_board'
class GroupC...etc.
What I really was looking for was a factory function to do this for me. I tried this sort of thing:
def makeBoard(group):
class Board(Posts):
class Meta(Posts.Meta):
db_table = group
return board #note I tried with and without this line
And then I ran a simple for loop using a list of groups.
for group in groups:
makeBoard(group)
I found myself hitting a RuntimeError: conflicting models in application, and I probably deserved it. So then I figured what I need is something like:
def makeBoard(group):
class group(Posts): #***group here being a variable, not the class name
class Meta(Posts.Meta):
db_table = '%s' % group #maybe issues here too, but the table
return group #name is not that important if the class
#name works
But I couldn't figure out how to make this work! Is there a way to pass a variable from a list to a class name?
Anyway if you're still with me I appreciate it. I've been on stackoverflow all day and while I've found guides for creating abstract base classes and subclasses to solve similar issues, I didn't see a way to create a function to do this for me. I ultimately punted here and just make a subclass for each group by hand. If there is a way to automate this process, I'd love to hear it.
Also, if I'm being stupid for not just going with one db table containing every post, I'd like to know that too, and why! Or if there's a better way to implement this kind of system altogether. I apologize if this has been answered before, I really couldn't find it.
Thank you!
Using a single table would not be bad practice. The extra memory is minimal, on modern systems that shouldn't be a problem. You shouldn't worry about performance either, premature optimization (not including the actual system design) is considered bad practice, but if you run into performance problems you can always specify an index on the group column:
group = models.CharField(max_length=100, db_index=True)
That's not to say that it is the best option, or that your method isn't good. Also, it is entirely possible to dynamically create models, using the type() built-in function. The only difference with dynamically creating models and creating other classes is that you must specifically pass the __module__ attribute. You can create subclasses for Posts in the following way:
def fabric(names, baseclass=Posts):
for name in names:
class Meta:
db_table = '%s_table' % name.lower()
attrs = {'__module__': baseclass.__module__, 'Meta': Meta}
# specify any other class attributes here. E.g. you can specify extra fields:
attrs.update({'my_field': models.CharField(max_length=100)})
newclass = type(str(name), (baseclass,), attrs)
globals()[name] = newclass
fabric(['GroupA', 'GroupB', 'GroupC', etc...])
Put that code in your models.py after your Posts class, and all classes will be created for you. They can be used in any way normal classes can be used: Django doesn't even know you dynamically created this class. Though your Meta class doesn't inherit from Posts.Meta, your meta settings should still be preserved.
Tested with Django 1.4.
Try smth like this
import app.models as group_models
from django.db.models.base import ModelBase
def fabric(group):
for item in dir(group_models):
c = getattr(group_models, item)
if type(c) is ModelBase:
if c._meta.db_table == '%s_table' % group:
return c
return None

Get FK set for each instance in queryset

I have a tree-like Django model say named A, which been done by django-mptt.
class A(MPTTModel):
parent = TreeForeignKey('self')
this class automaticly has the 'children' manager, so i can easily get the subtree
There is another model, which have FK link to A:
class SomeModel(models.Model):
link_to_a = models.ForeignKey(A)
I know, that if i want to get SomeModel set of A instance i can do that:
a = A.objects.filter(blah)
a.somemodel_set.all()
and the question is:
what is the most pythonic way to fetch somemodel_set of each instance in some queryset under A model, i.e. i want 4 example this:
some_A_instance.children.all().get_all_somemodel_instances()
and get_all_somemodel_instances() should retrieve ziped queryset of sets for each children
Do you just need the related items in one list, or do you need to associate each set with their parent? If the former, you can get them all at once with this:
related_items = SomeModel.objects.filter(link_to_a=some_A_instance.children.all())
which will do one single query (with a subquery) to get everything.
Otherwise, you can use prefetch_related() to get all items' related sets in one go:
items = some_A_instance.children.all().prefetch_related('somemodel_set')
This should do:
[child.somemodel_set.all() for child in some_A_instance.children.all()]

How do I access the child classes of an object in django without knowing the name of the child class?

In Django, when you have a parent class and multiple child classes that inherit from it you would normally access a child through parentclass.childclass1_set or parentclass.childclass2_set, but what if I don't know the name of the specific child class I want?
Is there a way to get the related objects in the parent->child direction without knowing the child class name?
(Update: For Django 1.2 and newer, which can follow select_related queries across reverse OneToOneField relations (and thus down inheritance hierarchies), there's a better technique available which doesn't require the added real_type field on the parent model. It's available as InheritanceManager in the django-model-utils project.)
The usual way to do this is to add a ForeignKey to ContentType on the Parent model which stores the content type of the proper "leaf" class. Without this, you may have to do quite a number of queries on child tables to find the instance, depending how large your inheritance tree is. Here's how I did it in one project:
from django.contrib.contenttypes.models import ContentType
from django.db import models
class InheritanceCastModel(models.Model):
"""
An abstract base class that provides a ``real_type`` FK to ContentType.
For use in trees of inherited models, to be able to downcast
parent instances to their child types.
"""
real_type = models.ForeignKey(ContentType, editable=False)
def save(self, *args, **kwargs):
if self._state.adding:
self.real_type = self._get_real_type()
super(InheritanceCastModel, self).save(*args, **kwargs)
def _get_real_type(self):
return ContentType.objects.get_for_model(type(self))
def cast(self):
return self.real_type.get_object_for_this_type(pk=self.pk)
class Meta:
abstract = True
This is implemented as an abstract base class to make it reusable; you could also put these methods and the FK directly onto the parent class in your particular inheritance hierarchy.
This solution won't work if you aren't able to modify the parent model. In that case you're pretty much stuck checking all the subclasses manually.
In Python, given a ("new-style") class X, you can get its (direct) subclasses with X.__subclasses__(), which returns a list of class objects. (If you want "further descendants", you'll also have to call __subclasses__ on each of the direct subclasses, etc etc -- if you need help on how to do that effectively in Python, just ask!).
Once you have somehow identified a child class of interest (maybe all of them, if you want instances of all child subclasses, etc), getattr(parentclass,'%s_set' % childclass.__name__) should help (if the child class's name is 'foo', this is just like accessing parentclass.foo_set -- no more, no less). Again, if you need clarification or examples, please ask!
Carl's solution is a good one, here's one way to do it manually if there are multiple related child classes:
def get_children(self):
rel_objs = self._meta.get_all_related_objects()
return [getattr(self, x.get_accessor_name()) for x in rel_objs if x.model != type(self)]
It uses a function out of _meta, which is not guaranteed to be stable as django evolves, but it does the trick and can be used on-the-fly if need be.
It turns out that what I really needed was this:
Model inheritance with content type and inheritance-aware manager
That has worked perfectly for me. Thanks to everyone else, though. I learned a lot just reading your answers!
You can use django-polymorphic for that.
It allows to automatically cast derived classes back to their actual type. It also provides Django admin support, more efficient SQL query handling, and proxy model, inlines and formset support.
The basic principle seems to be reinvented many times (including Wagtail's .specific, or the examples outlined in this post). It takes more effort however, to make sure it doesn't result in an N-query issue, or integrate nicely with the admin, formsets/inlines or third party apps.
Here's my solution, again it uses _meta so isn't guaranteed to be stable.
class Animal(models.model):
name = models.CharField()
number_legs = models.IntegerField()
...
def get_child_animal(self):
child_animal = None
for r in self._meta.get_all_related_objects():
if r.field.name == 'animal_ptr':
child_animal = getattr(self, r.get_accessor_name())
if not child_animal:
raise Exception("No subclass, you shouldn't create Animals directly")
return child_animal
class Dog(Animal):
...
for a in Animal.objects.all():
a.get_child_animal() # returns the dog (or whatever) instance
You can achieve this looking for all the fields in the parent that are an instance of django.db.models.fields.related.RelatedManager. From your example it seems that the child classes you are talking about are not subclasses. Right?
An alternative approach using proxies can be found in this blog post. Like the other solutions, it has its benefits and liabilities, which are very well put in the end of the post.

Categories

Resources