Consider these models. Note that Parent is a foreign key of ABSTRACT children.
class Parent(models.Model):
name = models.CharField()
class AbstractChild(models.Model):
parent = models.ForeignKey(Parent)
class Meta:
abstract = True
class Child1(AbstractChild):
special1 = models.CharField()
class Child2(AbstractChild):
special2 = models.CharField()
Using the admin in obvious ways, Child1 objects will have a Parent field that lets them be associated with their single parent. Good so far.
But I want to go the other direction too. That is, I want Parent to have a <select multiple>
menu that allow choosing zero, one, or more Child objects. I know MantToManyFields will all this kind of selection. But I don’t want to change my data model (don’t want to change ForeignKey to ManyToManyField) because there really can only be zero or one Parent for any child.
In my real app I have 7 different AbstractChild classes. I’m willing to have to modify each and or add 7 different somethings to the Parent.
Is this possible? How?
Welcome @pothiers !
The first thing to keep in mind is that an abstract parent model is not a model. It’s a syntactical device to allow common definitions to be shared without being repeated.
As a result, what you end up with are separate foreign key fields in each of Child1
, Child2
, …, Child7
. This means you have different reverse relationships from Parent
to these Childx
classes. Or, in other words. if you have an instance of Parent
named Parent
, then parent.child1_set.all()
are the set of Child1
related to that parent, parent.child2_set.all()
being the set of Child2
, etc.
This also means that the primary keys between Child1
, Child2
, etc aren’t unique. Any instance of Child1
could have the same pk as any (or all) of the other Childx
models.
So, to work with the structure as you’ve defined it here, you could create an instance of a MultipleChoiceField
where you create the choices yourself with the values containing both the model name and primary key. (e.g., define the values of the options for that field as something like "Child1:1", "Child1:2", "Child2:1"
, etc.) It would then be up to you to process the list of selected items to split them apart and set the foreign key field of those items.
Thanks for taking the time for a thoughtful answer! I knew I could do parent.child1_set.all() to get a list of children. I did not see how to get Admin to use such a thing. I’ll try using MultipleChoiceField. I certainly didn’t think about the fact that children could have the same pk. But in my case I don’t think that will be a problem. I’ll have one MultipleChoiceField for each type of child in parent. The result of each MCF selection will be used to set the objects for a child class specific relation. So, no ambiguity there.
I don’t see how I’ll actually get my MCF results and assign to a field in the parent object. I’ve done something like that for hand build forms, but never for Admin. I haven’t probed the depths of Admin documentation, so maybe I’ve glossed over something.
If anyone can point to where documentation that explains how to use MultipleChoiceField in admin to set the object being changed, I would appreciate it.
Ok, that’s not how I interpreted your original post - but that does make it simpler.
A form is a form. The admin allows you to specify the form to use for a model.
Since these desired form fields are not fields on the parent model, it’s going to be a regular field in the form, and not one of the form fields created by the model form class.
This means it’s going to be up to you to handle the saving of the selections from those fields from within your form.
Side note: This might end up being one of those cases where using the admin is not beneficial, and that it may be easier just to create your own view for this. (See the first two paragraphs in the docs at Django admin.)
What you are trying to do can only be done with a many-to-many field.
Foreign keys are fields of the child model, and I think it is wrong to manage them in the parent model.
In the parent model, what you can do should be limited to finding children that have you as their parent to avoid other problems later.
If some data has changed and you need to change the dependent parent object, do the following in the shell:
> python manage.py shell
p = Parent.objects.get({something})
c = Child.objects.filter(parent=p).update(parent_id={new_pk})
The possibility that admin (or at least not “easy admin”) might not be appropriate is what drove me here. I’m working on the MVP (Minimum Viable Product) version of something now so am trying to avoid anything even a little fancy until MVP is done. For me using a form I create in admin qualifies as “a little bit fancy”.
Comments here are very useful in helping draw the line between what I do now and what I do after MVP.
I don’t know about it being the ONLY way. But I recognize many-to-many is one of the more obvious ways. As I said in my initial problem setup, I don’t want to use many-to-many because it will make other developers think there is truly a many-to-many relationship when the actual data model demands a many-to-one relationship.
I realize I could do things to force the many-to-many to only have one object on one side, but I think that will be setting things up for misunderstandings later.
Then you have a decision to make.
- Push this functionality to post-MVP
- Get “a little bit fancy”.
Making the decision to alter your data models to accomodate the limitations of the admin is fundamentally the wrong decision to make, as it will likely have adverse effects in the future.
Agreed, I won’t alter data model (except maybe if its for MVP with a change scheduled after MVP to revert and implement one of the alternatives).
I’m arguing for pushing to post-MVP, but product owner gets to decide when they know now much it would cost. Its even possible that the product owner will change workflow to make the whole issue vanish.
Thanks @KenWhitesell for helping to clarify my thinking!
I don’t know what you’re thinking, but if you manage a many-to-many relationship on the server, hiding it from the form is a simple matter.
Yep, I know I can hide aspects in the form. What I’m thinking is that my data model says there is exactly one parent per child. But there are 0 or more children per parent. If I used many-to-many it would mean there could be any number of parents per child. I don’t want the data model (models.py) to allow something that is illegal in my app. Other developers will be using the data model. They may assume that the data model represents what is allowed – leading them to write code that is in error.
Do you know the through model?
This allows you to use the unique option to restrict child models to creating only one relationship.