Linking to parent in many to one relationship

I have two models in a one to many relationship. I am editing a parent record and creating a child. When I create the child I cannot figure out how the send a reference of the parent so that I can instantiate the ForiegnKey of the child to point to the parent. Could anyone help. thanks

The parent is:

class Site(models.Model):
    name = models.CharField(max_length=100)
    address1 = models.CharField(max_length=100)
    address2 = models.CharField(max_length=100)
    postcode = models.CharField(max_length=50)
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="sites")

    def __str__(self):
        return f"{self.name}, {self.postcode}"

    def get_queryset():
        return set.request.user.sites.all()

the child is:

class Administrator(models.Model):
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    telephone = models.CharField(max_length=50)
    email = models.EmailField()
    site = models.ForeignKey(
        Site, on_delete=models.CASCADE, related_name="adminstrators"
    )

    def __str__(self):
        return f"{self.first_name} {self.last_name}, {self.email}"

I am trying to point the child at the parent in the child’s validation function:

def form_valid(self, form):
    self.object = form.save(commit=False)
    self.site = # I don't know what to put here as I have to reference to the parent Site object
    self.object.save()
    return HttpResponseRedirect(self.get_success_url())

to reference the data through the foreign key, you could do this:

a = Administrator.objects.get(first_name = "Bob")

then:
a.site would point to the parent model

and you could do:
a.site.name
a.site.address1
a.site.address2

Helpful resource:
https://django-book.readthedocs.io/en/latest/chapter10.html

Why is it that you need to do this?:

def form_valid(self, form):
    self.object = form.save(commit=False)
    self.site = # I don't know what to put here as I have to reference to the parent Site object
    self.object.save()
    return HttpResponseRedirect(self.get_success_url())

And is this to do with updating a form, or creating a form?

I was viewing a Site detail form, and linked to create an Administrator for that Site. How else can make the Administrator a child of a particular Site - I don’t want to select the Site from a list.

The problem is that i am trying to set the value for a.Site not read it. I am creating the Administrator and wanting to put a reference to the Site in it.

The many-to-one relationship is a good way to link them I think (like how you are doing)

You can always have a reference for the site if you change the object name perhaps.

Something like this could possibly work to keep a reference to the parent model:

class Administrator(models.Model);
     #fields

   def __str__(self):
      return str(site.postcode)

I can’t think of anything else, maybe someone else can help also - just wait as people are living in different timezones

Sorry, my explanation is not clear. I can’t keep a reference to the Site object because the Administrator object doesn’t know what the Site should be. My problem is: how do I tell the Administrator object what Site object it belongs to programmatically, rather than having the user select it from list of existing Sites. Thank you for your help, but I haven’t got there yet. I am an absolute beginner with Django.

what I would do is create a ManyToMany model called “Site list” which Site should link to, and this would go in a main model (call it “MainModel”):

site_list = models.ManyToManyField('Site_list', blank=True)

class Site_list(models.Model):

    name = models.CharField(max_length=255, default='')
    id = models.UUIDField(default=uuid4, unique=True, primary_key=True, editable=False)

    def __str__(self):
        return self.name

then link the Site model to this one:

class Site(models.Model):
    name = models.CharField(max_length=100)
    address1 = models.CharField(max_length=100)
    address2 = models.CharField(max_length=100)
    postcode = models.CharField(max_length=50)
    Site_list = models.ForeignKey(Site_list, null=True, on_delete=models.PROTECT, verbose_name="Site selection")

    def __str__(self):
        return f"{self.name}, {self.postcode}"

    def get_queryset():
        return set.request.user.sites.all()

then register the models in admin.py:

admin.site.register(MainModel)
admin.site.register(Site_list)

this would involve selecting the sites from a list though and I don’t think that’s what you wanted to do. I’m not sure of other ways to go about it because you need a context for the site. Maybe there are other ways around this but I’m not sure

Thank you for that. What I have at the moment works if I don’t mind selecting from a list. But I really don’t want to do that.

For clarity, are you performing this operation in a view that you have written, or are you looking at doing this in the Django admin?

It would be helpful if you posted the complete view.

[Edit] In general terms if these operations are being performed on two separate pages (two different views), you would generally pass the parent’s PK to the child view as a parameter in the url. The appropriate fix for this is likely going to involve changes to the parent’s view and template, and the child’s url definition.

I know this must be very easy because its so fundamental. I realise now that is is through the url. Which I now have as :

path('administrator/new/<int:site_id>',views.AdministratorCreateView.as_view(),name='administrator.new'),

The Site detail html makes the request using:

<a href="{% url 'administrator.new' site.id %}" class="btn btn-primary">Create</a>

If i was using a view function I would pass site_id s a parameter:

def adminstration_view(request, site_id):

but since I am using a view class i am not sure how to access the site_id. The view class is as follows:


class AdministratorCreateView(CreateView):
    model = Administrator
    form_class = AdministratorForm
    template_name = "register/administrator_new.html"
    
    def get_success_url(self):
        return reverse_lazy("administrator.detail", kwargs={"pk": self.object.pk})

    def form_valid(self, form):
        self.object = form.save(commit=False)
        self.object.save()
        return HttpResponseRedirect(self.get_success_url())

Thanks

All named url parameters are made available in a CBV through the self.kwargs object. See the docs and examples at Built-in class-based generic views | Django documentation | Django.

ok, I finally have it. With the url and http as previously noted by AdministratorCreateView class is as follows:


class AdministratorCreateView(CreateView):
    model = Administrator
    form_class = AdministratorForm
    template_name = "register/administrator_new.html"
    
    def get_success_url(self):
        return reverse_lazy("administrator.detail", kwargs={"pk": self.object.pk})

    def form_valid(self, form):
        site_id = self.kwargs['site_id']
        self.object = form.save(commit=False)
        self.object.site = Site.objects.get(pk=site_id)
        self.object.save()
        return HttpResponseRedirect(self.get_success_url())

And that seems to do what I want.
Thanlyou everyone for your help!

Note: This query really isn’t necessary.

You could do self.object.site_id = self.kwargs['site_id']. It’s not necessary to retrieve the referenced object to do the FK assignment.

Thank you, that seems to work, although I don’t quite understand why since I don’t have a field ‘site_id’ on the Administrator object; is it implied by the ‘site’ object?

It’s documented behavior of the ForeignKey field - see Model field reference | Django documentation | Django

Thank you. I’d just found it in the docs.
Thanks again for your help.