user3541631
user3541631

Reputation: 4008

Automatically save a foreign key in Django admin;

I have 2 models A and B; When a B instance is created in django Admin, I want the foreign key to be automatically added.

In the examples below I try to use a custom form, but I tried also to use the save in the model, but there a ValidationError is not raised

I have the following code:

in models:

class A(models.model):
    content = models.TextField()

Class B(models.model):
    a = models.ForeignKey(A, related_name='bs', on_delete=models.CASCADE)
    content = models.TextField()

in form:

class AdminBModelForm(forms.ModelForm):

    class Meta:
        model = B
        fields = ['content']

    def save(self, commit=True):
        obj = super().save(commit=False)
        obj.a_id = A.objects.first()
        if obj.a_id is None:
            raise ValidationError('You first need to set A')
        obj.save()
        return obj

in admin:

  class BAdmin(admin.ModelAdmin):
        model = B
        form = AdminBModelForm

The error: IntegrityError - null value in column "_id" violates not-null constraint

Upvotes: 0

Views: 1901

Answers (2)

Alasdair
Alasdair

Reputation: 309089

Firstly, note that you shouldn't assign model objects to the a_id field. You can either do:

obj.a = A.objects.first()

or:

a = A.objects.first()
if a is not None:
    obj.a_id = a.id

Next, if you cannot guarantee that A.objects.first() is not None, then you need to check that in the clean method. It is too late to raise validation errors in the save or save_model methods.

class AdminBModelForm(forms.ModelForm):

    def clean(self):
        cleaned_data = super().clean()
        if not A.objects.exists():
            raise forms.ValidationError("Cannot save B until an A has been created")
        return cleaned_data

Finally, a common approach so set values in the Django admin is to override the save_model method.

class BAdmin(admin.ModelAdmin):
    model = B
    fields = ['content']

    def save_model(self, request, obj, form, change):
        obj.a = A.objects.first()
        super().save_model(request, obj, form, change)

Upvotes: 4

Harsh
Harsh

Reputation: 253

There is a slight error in assigning A object. Try this:

class AdminBModelForm(forms.ModelForm):
    class Meta:
        model = B
        fields = ['content']

    def save(self, commit=True):
        obj = super(AdminBModelForm, self).save(commit=False)
        obj.a = A.objects.first() # Assigning the object reference
        if not obj.a: # No need to check for None explicitly
            raise ValidationError('You first need to set A')
        obj.save()
        return obj

Upvotes: 0

Related Questions