Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make ForeignKey optional in Django Admin?

I have a custom django admin page, and I would like to make the two ForeignKey fields optional in the admin interface. I do not want to change the underlying model.

This is the model:

class IncorporationTicket(models.Model, AdminURL):
    ordered_by = models.ForeignKey('Organisation', # organisation which ordered this
                                   null = True,
                                   blank = False, # i.e. can only be null as a result of delete
                                   on_delete = models.SET_NULL)
    ordered_by_individual = models.ForeignKey('Individual', # individual at organisation which ordered this
                                               null = True,
                                               blank = False, # i.e. can only be null as a result of delete
                                               on_delete = models.SET_NULL)

(AdminURL is a mixin which provides get_absolute_url)

This is the ModelAdmin:

class TicketAdmin(admin.ModelAdmin):
    readonly_fields = ('ordered', 'charge', 'amount_paid', 'submitted_on')

    formfield_overrides = {
        models.ForeignKey: {'required': False},
        }


    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        pk = resolve(request.path).args[0] # the request url should only have one arg, the pk
        instance = self.get_object(request, pk) 
        user = request.user
        kwargs['required'] = False # will be passed to every field

        if db_field.name == "ordered_by_individual":
            # queryset should be a union of (a) individual already set on object (b) individual for current user
            ## None option is provided by admin interface - just need to let field be optional.
            if instance.ordered_by_individual:
                kwargs["queryset"] = (
                    Individual.objects.filter(pk = instance.ordered_by_individual.pk) |
                    user.individual_set.all())
            else: kwargs["queryset"] = user.individual_set.all()
        elif db_field.name == "ordered_by":
            # queryset should be a union of (a) organisation already set (b) any organisations for which user is authorised

           try:
               individual = user.individual_set.all()[0]
               all_orgs = Organisation.all_organisations_for_which_individual_authorised_to_incorporate(individual)
           except:
               all_orgs = Organisation.objects.none()
           if instance.ordered_by:
               kwargs["queryset"] = (
                   Organisation.objects.filter(pk = instance.ordered_by.pk) |
                   all_orgs)
           else: kwargs["queryset"] = all_orgs

        return super(type(self), self).formfield_for_foreignkey(db_field, request, **kwargs)

As you can see, I have tried to use both formfield_overrides, and formfield_for_foreignkey to set required = False on the FormField, but it is not having the required effect: when attempting to save through the admin interface without setting (that is, leaving the field in its original blank state), the admin interface shows the error 'This field is required.'

So, my question is: how does one prevent the underlying form from requiring certain fields, while still also setting the choices in formfield_for_foreignkey?

like image 265
Marcin Avatar asked Dec 30 '11 17:12

Marcin


People also ask

How do I make a field optional in Django admin?

In order to make a field optional, we have to say so explicitly. If we want to make the pub_time field optional, we add blank=True to the model, which tells Django's field validation that pub_time can be empty.

How do I make ForeignKey not required in Django?

To make a foreign key field in Django non-nullable, you need to provide a default value for the existing rows. You might want to also provide a default value for new rows. Adding a default is easy when the value is a string or a date, but not when you have to provide the ID of another object.

What is the difference between ForeignKey and OneToOneField?

A one-to-one relationship. Conceptually, this is similar to a ForeignKey with unique=True , but the "reverse" side of the relation will directly return a single object. In contrast to the OneToOneField "reverse" relation, a ForeignKey "reverse" relation returns a QuerySet .

Can ForeignKey be null Django?

ForeignKey does not allow null values.


2 Answers

While I'm not sure why kwargs['required'] wouldn't work, you could always override the admin form with your own form. It hasn't failed me with magical django admin behavior so it's a pretty good bet.

class MyForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        self.fields['my_fk_field'].required = False

    class Meta:
         model = MyModel

class MyAdmin(admin.ModelAdmin):
    form = MyForm

This would still allow you to modify the QuerySet via the formfield_for_foo methods.

like image 87
Yuji 'Tomita' Tomita Avatar answered Oct 16 '22 05:10

Yuji 'Tomita' Tomita


... almost 9 years later, in Django v3.1.2 ...
blank=True works fine for me:

from django.contrib.auth.models import User

owner = models.ForeignKey(User, 
                          related_name="notes",
                          on_delete=models.CASCADE, 
                          null=True, 
                          blank=True)

(the solution has been taken from here)

like image 36
Александр Avatar answered Oct 16 '22 03:10

Александр