简体   繁体   中英

Many to one relationship in Django

I have two models

class Model_1(models.Model):
    foreign_key_field = models.ForeignKey(Model_2)

class Model_2(models.Model):
    number = models.IntegerField()

I need a ModelForm to edit a Model_2 object. In that form I want a list of all Model_1 objects and the option to checkbox those Model_1 fields that should use the specific Model_2 object as ForeignKey.

Actually, it should work something like a ManyToMany field, but in a ManyToOne relationship.

Maybe the ModelForm could look something like this:

class SetForeignKeyForm(ModelForm):
    is_linked = BooleanField(required=False)

    class Meta:
        model = ModelName
        fields = ()

    def clean_is_linked(self):
        if self.cleaned_data['is_linked']:
            self.fields['foreign_key_field'] = self.object

and the view:

class Model2UpdateView(ModelFormSetView):
    model = Model_2
    form_class = SetForeignKeyForm

    def get_queryset(self):
        return Model_2.objects.all()

I don't know how to get the Model_2 object in my custom ModelForm, so I don't know how to save whether the Model_1 object is linked to the specific Model_2 object or not.

I will end up with a output which looks something like

<h1>Model_2 object</h1>
<form method="post" action="">
  <tr>
    <td>Model1 object name</td>
    <td><input type="checkbox" name="model1_id_1" /></td>
  </td>
  <tr>
    <td>Model1 object name</td>
    <td><input type="checkbox" name="model1_id_2" checked="checked" /></td>
  </td>
  <tr>
    <td>Model1 object name</td>
    <td><input type="checkbox" name="model1_id_3" /></td>
  </tr>
  <input type="submit" />
</form>

Edit 1

I have made something that seems to work, but I'm not sure if it's correctly done.

I have a view

class Model2Detail(ModelFormSetView):
    model = Model1
    template_name = 'tpl.html'
    form_class = PairingForm
    extra = 0

    def get_queryset(self):
        self.object = get_object_or_404(Model2, slug=self.kwargs['slug'])
        return Model1.objects.all() # those objects with ForeignKey field

    def formset_valid(self, formset):
        for form in formset:
            form.instance.foreign_key_field = self.object # set Model1's ForeignKey field
            form.save()
        return HttpResponseRedirect(self.get_success_url())

    def get_success_url(self):
        return self.object.get_absolute_url()

and a form

class PairingForm(ModelForm):
    is_linked = BooleanField(required=False)

    class Meta:
        fields = ()
        model = Model2 # those objects __without__ ForeignKey field

    def __init__(self, *args, **kwargs):
        super(PairingForm, self).__init__(*args, **kwargs)
        if self.instance.foreign_key_field: # if object already paired
            self.fields['is_linked'].initial = True

    def save(self, commit=True, force_insert=False, force_update=False, *args, **kwargs):
        obj = super(PairingForm, self).save(commit=False, *args, **kwargs)
        if self.cleaned_data['is_linked']:
            obj.foreign_key_field = self.instance.entry
        else:
            obj.foreign_key_field = None # remove pairing
        obj.save()

To me it seems wrong that I try setting the ForeignKey field in both the view and the form, but I don't know how it should be.

Edit 2

My code works, but I actually don't know why. If I am not setting the foreign key field in formset_valid , it doesn't work any longer, so I think something is wrong. Shouldn't I be able to make it work without overwriting formset_valid at all? Am I using the right model in Meta.model in my form?

I think it is good practice to modify only one type of object in a form. This keeps things separated and, I believe, leads to a cleaner design with expected behaviors.

With that in mind, I think you should think of this not as "editing a Model_2 object" but more as "editing a bunch of Model_1 objects" by setting or unsetting a value on each one. In that architecture, you might use a form set, each with a checkbox field (say, is_linked ). In the formset's save_new or save_existing method, you could check that field and set the foreign key accordingly on the particular object being saved.

Your updated version looks pretty good. The only problem I see is that your formset_valid method seems to set the foreign key field for every object in the form, but you really only want that to be set when is_linked is True, right? It seems to me that ParingForm actually does the right thing on save, so you shouldn't need to set the value in formset_valid .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM