Django method for loading Foreign Key objects - django

Suppose I have model Parent that has a ForeignKey to model Child:
class Parent(models.Model):
child = models.ForeignKey(Child, related_name='parents')
class Child(models.Model):
some fields here
I know that if I have a Parent object and want to get the related Child object, I can simply use:
childObj=parent.child
If child has not already been loaded, the above will make another query to the DB to instantiate the Child in parent.child.
But, suppose that every time I want to do something like this, I also want to do some additional processing. In other words, whatever method gets called when I invoke parent.child is a method I want to override so I can do additional processing. Does anyone know what this method is or how I can accomplish this?

You could make a child property.
class Parent(models.Model):
_child = models.ForeignKey(Child, related_name='parents')
#property
def child(self):
child = self._child
# extra processing here
return child
class Child(models.Model):
some fields here

Related

django models-design: "ptr field is required"

I'm using Python 3.6+PostgreSQL 10+latest Django and DjangoRestFRamework. I have the following models, in which several models inherit from a class which is the ForeignKey (One-to-Many) of another class.
class Voteable(models.Model):
Voteable_id = models.BigAutoField(primary_key=True);
class base(Voteable):
class Meta:
abstract = False
class traslated_info(models.Model):
info_about=models.ForeignKey(base)
info_body=models.TextField()
info_language=models.CharField(max_length=2)
class A(base):
A_id=models.BigAutoField(primary_key=True)
A_field=models.TextField()
class B(base):
B_id=models.BigAutoField(primary_key=True)
B_field=models.TextField()
B_belongs_to=models.ForeignKey(A)
class C(base):
C_id=models.BigAutoField(primary_key=True)
C_field=models.TextField()
C_belongs_to=models.ForeignKey(A)
C_belongs_to=models.ForeignKey(B)
Whenever I try saving an object A (via curl), django says that base_ptr is required. I don't know how to model this situation. The end user is not expected to create item base and then item A, B or C. I tried class base as abstract, but an abstract class can't be ForeignKey. I want to automatically create a base class whenever a class A is created.
I think I have two options: A) Remove the ForeignKey and store the language-specific info fields as HStoreField. This makes the code somewhate dependent on Postgree. B) Create some sort of routine that automatically creates parent base item whenever a child A item is created (preserving the one to one relationship).
What do you recommend? Is there some django easy option I'm missing to make option B? I have not found this. Thank you.
Having an autofield as primary_key in the models A, B or C causes this error, as creating a child model doesn't cascade-create parents.
I found two workarounds:
Change autofield option primary_key to false and add
SILENCED_SYSTEM_CHECKS=['fields.E100']
Overriding Viewset create method:
#transaction.atomic
def create(self,request,*args,**kwargs):
request.data['base_ptr'] = base.objects.create(user=request.user,created_date=datetime.utcnow()).pk
return super(viewsets.ModelViewSet,self).create(request,*args,**kwargs)
I will stick with the second, I'm quite sure more issues will arise.
Make your serializer as below, you dont need to create base classes explicitly, it will be created automatically.
class ASerializer(serializers.ModelSerializer):
class Meta:
model = A
read_only_fields = ('base_ptr',)
fields = '__all__'

Django ForeignKey limit_choices_to multiple child elements

I have the following models:
class Person(models.Model):
# fields
class Teacher(Person):
# fields
class Student(Person):
# fields
teacher = models.ForeignKey(teacher)
class Staff(Person):
# fields
class SomeModel(models.Model):
# fields
point_person = models.ForeignKey(Person)
But I want to limit my "point_person" to Teacher and Student only. How can I do this?
I would offer to implement a custom manager, probably overriding the get_queryset.
I can see 2 solutions to get only those Parent, who have ChildA and/or ChildB.
If one Parent never has ChildA and ChildB same time (or any other combination), you can add an extra field (db column) to Parent, which indicates what is the class of its child object, if any. In get_queryset of your custom manager you always check this field.
If one Parent can have multiple classes of Child simultaneously, of if you don't want to add an extra column, then you override get_queryset, to actually select from ChildA and ChildB, and combine the querysets afterwards into a single queryset.

Django ordering parent model by count of children models in (one to many relation)

Suppose we haw django models Parent, and Child. Child belongs to one Parent and one Parent can have multiple children.
class Parent(models.Model):
pass
class Child(models.Model):
parent = models.ForeignKey(Parent)
I would like to obtain set of all parents ordered by the number of children.
child_set is the default related_name of your parent field in the Child model. If you've specified a different one, you will have to change the code accordingly.
from django.db.models import Count
ordered_parents = Parent.objects.annotate(num_children=Count('child_set')).order_by('-num_childen')
Hope it helps.
read up on aggregate functions in django docs
and in any case, you can do parent_instance.child_set.count() to get the number of children
and if i'm not mistaken you can filter and also order_by that relation.
here's a link for reverse relations

django: model inheritance and validators in admin

I have an abstract model that is inherited by 2 children. In the children, one is setting a validator. When I run the code, I see both children having the validator.
Here is the pseudo code:
class myAbstract(models.Model):
Intro = models.TextField(blank=True, null=True, )
class Meta:
abstract = True
class child1(myAbstract):
class Meta:
verbose_name = 'child 1'
class child2(myAbstract):
def __init__(self, *args, **kwargs):
super(child2, self).__init__(*args, **kwargs)
intro = self._meta.get_field('Intro')
intro.validators.append(MaxLengthValidator(60))
class Meta:
verbose_name = 'child 2'
In the admin if I add a child1 and then add a child2 then the validator kicks in for child2 and limits the number of characters. If I start with child2 then child2 doesn't get the validator.
Is this the expected behavior? If there, what is the suggested way of coding this? I thought about moving Intro to the child classes.
Solved:
As Alasdair pointed out the validators is a class variable therefore this is the expected behavior.
I tried moving the Intro field to the child but that didn't work. I used this solution:
https://stackoverflow.com/a/3209550/757955 that sets forms.CharField in the modelform.
The validators are not set per model instance. When you append the MaxLengthValidator, you are altering the intro field of the parent class.
I don't think there's an easy way around this. You could write a clean() method for each child model, and perform the validation there. However, I think that moving the intro field into the child classes is probably the best option here.
I did not really expect this behaviour, but there is a lot of magic happenign in the background with models. I see 2 solutions:
Change the instance, not the class. _meta is an class variable, thus _meta.get_field will return class attributes. I would rather try to manipulate the instance fields like so
def init(...):
self.intro.validators.append(MaxLengthValidator(60))
If 1 does not work, or you do not like it, leave the models alone, i.e. do not add the validator, but add the validators to the form that you use for the models. There you have more flexibility and can do what you want.

Displaying specific records in a queryset from within Django template

Is it possible to access a specific record of a QuerySet based on a value in one of its fields -- from within the template?
Let's say I have the following models:
Parent:
someField
Child:
parentFK = ForeignKey(Parent)
bar = CharField
If Parent has many children, and I pass the QuerySet of Parent.objects.all() to my template, is it possible to access something like: Parent.someField.bar where foo=4?
In otherwords, access and display the field bar for the record where foo holds the value 4? I only want to pass the Parent QuerySet to the template.
Thanks!
You can do it using custom template tag.
P.S. Is your model description correct? If you have Parent with many children is it ok, that parent contains a link to child, not vice-versa?