maddersky maddersky - 4 months ago 62
Python Question

Django nested admin: how to set foreign key to grandparents'id?

I have a django project where I use nested-admin, and I'm trying to figure out how to set the foreign key of an element to its grandparents' id.

I have the following

model.py
:

class Person(models.Model):
name = models.CharField(max_length=80)

def __str__(self):
return self.name

class Job(models.Model):
location = models.CharField(max_length=20, null=True, blank=True)
person = models.ForeignKey(Person)


class Project(models.Model):
name = models.CharField(max_length=20, null=True, blank=True)
job = models.ForeignKey(Job, null=True)
person = models.ForeignKey(Person)


Project
's FK to
Job
can be NULL for the simple reason that a project is not necessarily related to a Job (i.e. personal project).

admin.py
:

class ProjectInline(NestedStackedInline):
model = Project
extra = 1


class JobInline(NestedStackedInline):
model = Job
inlines = [ProjectInline]
extra = 1


@admin.register(Person)
class PersonAdmin(NestedModelAdmin):
model = Person
inlines = [JobInline]


Currently, the
Person
form looks like this:

Current look. See Person field[2]

The fact that we can choose the
Person
in
Project
doesn't make any sense. I want to attach it directly to the
Job
's person.

I believe I have to customize a form or a view, and looked into
UpdateView
(like this link), but I haven't manage to do what I want.

Answer

Answer to the question asked

I managed to get something working using the nested_admin source code.

First, exclude the person field from the project form:

class ProjectInline(NestedStackedInline):
    # [...]

    exclude = ["person"]

In PersonAdmin, add the following code:

def save_formset(self, request, form, formset, change):

    for form in formset.forms:
        for job_form in form.nested_formsets:
            job_form.instance.person = form.instance.person
            for project_form in job_form:
                project_form.instance.person = form.instance.person

    super(PersonAdmin, self).save_formset(request, form, formset, change)

This way, the person field won't be displayed, preventing the user to change it, and it will automatically be set to the same value as the Job's Person you're modifying.

New problem appears!

However, another problem will appear if you try to add ProjectInline to Person:

The problem you'll encounter

As you can see, the Project "website" is both displayed inside the Job subset and the Person.

Best solution

After a lot of work, you will probably manage to do exactly what you want.

But in my opinion, the easiest solution would be to create two different Project model, both inheriting from the same base. You'd have a PersonnalProject, with a FK to Person, and a JobProject, with a FK to Job.

It will take more place in database but is so much easier to handle afterwards.