Django ForeignKey limit_choices_to multiple child elements - django

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.

Related

How can I access parent model fields in a form using the child model?

For context, I'm trying to create a form that allows users to upload info about their own custom Pokemon. Basically, they are creatures that you can catch, name, and level up. To draw a comparison, it is a similar concept to dogs; there are labradors, German Shepherds, huskies, etc. that would be variations of a base Dog model, but then each individual would have a name and other defining characteristics.
I've created Pokemon and CustomPokemon models and imported the latter into my forms.py file. I'm trying to access some parent fields but am unable to:
from django import forms
from .models import CustomPokemon
class PokemonForm(forms.ModelForm):
class Meta:
model = CustomPokemon
fields = ['pokemon.poke_name', 'name', 'level']
The poke_name field is inherited from the parent Pokemon model while the other two fields belong to the CustomPokemon model. I'm getting this FieldError:
Unknown field(s) (pokemon.poke_name) specified for CustomPokemon.
The issue isn't resolved by using poke_name, so I'm curious how I can access the parent model's fields so they can be displayed in the form.
First option
If you just want a dropdown displaying the field poke_name, what you could do is to define a __str__ method inside Pokemon model like this:
class Pokemon(model.Model):
...
def __str__(self):
return self.poke_name
Then, you can define the form as follows:
class PokemonForm(forms.ModelForm):
class Meta:
model = CustomPokemon
fields = ['pokemon', 'name', 'level']
And you will get a dropdown displaying all the poke_name of your database, where you can choose your foreign key. It would be better if poke_name is a unique field so that the foreign key can be clearly identified.
Second option
If you need more freedom, you could manually define a custom field as follows:
class PokemonForm(forms.ModelForm):
poke_name = forms.CharField()
class Meta:
model = CustomPokemon
fields = ['name', 'level']
Then, when validating the form, you should take care of whether the entry exists and/or create it:
if form.is_valid():
form.instance.pokemon = Pokemon.objects.get_or_create(cname=form.cleaned_data['poke_name'])
form.save()

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 method for loading Foreign Key objects

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

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.

Django: multiple table inheritance doesnt allow me to access derived class from base class

I want to get a derived modelclass instance from an instanciated base modelclass.
I have the following model class hierarchy:
class AlfrescoPFCModel(models.Model):
#some fields and methods
class Meta:
abstract = True
class Contenido(AlfrescoPFCModel):
#some fields and methods
class Proyecto(Contenido):
#some fields and methods
class ProyectoCalificado(Proyecto):
#some fields and methods
class ProyectoArchivado(ProyectoCalificado):
#some fields and methods
And I instantiate a Proyecto class in this way:
proyecto = proyecto_form.save(commit=False)
#do some stuff with some fields that dont appear on the form
proyecto.save
In another view I try to access the derived class ProyectoCalificado from the base class Proyecto previously instanciated and saved in the database doing:
pc = ProyectoCalificado.objects.get(pk=id)
and i get: ProyectoCalificado matching query does not exist.
I also tried:
p = get_object_or_404(Proyecto, id=id)
pc = p.proyectocalificado
but it get the same error in the second line.
Reading the documentation i should be allowed to do that:
#multi-table-inheritance
What i want to do is to incrementally complete the data associated to a Proyecto (project) following this workflow: Proyecto -> ProyectoCalificado -> ProyectoArchivado.
I have 3 different forms for each step. I need 3 different models because I need to save them in the database without filling all the mandatory fields at once.
Thanks!
Use Form Wizards (https://docs.djangoproject.com/en/dev/ref/contrib/formtools/form-wizard/).
UPDATE
If you can't use Form Wizards because of the situation you describe, then you should make the models fields blank or nullable at the database level and then only enforce field requirements on each individual form. Creating three levels of inheritance solely for the purpose of the single-time set of forms required to create it is absolutely the wrong approach. It only fragments your data across additional tables and makes querying more complicated with no long-term benefit.
So, for example. Set the model itself up as though nothing (or only the items in the first form) are required. Then, in your first form, only make the fields necessary for that particular stage required. You can do this easily by overriding the __init__:
class FirstForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(FirstForm, self).__init__(*args, **kwargs)
self.fields['some_required_field'].required = True
# rinse and repeat
Do the same in your second and third forms, again making on the actual fields that are required for that particular form, required.
Then, call it day and have a drink.
If you have the child instance, it should have a <base class name>_ptr member which points to the instance of its superclass. You can use this as the basis of a filter query to retrieve the child.
You can also just assume that instances of the base and derived class will have the same id if you haven't done anything to affect how ids are allocated.