48

I've got a model similar to this:

class Product(models.Model):
    third_party_id = models.CharField(max_length=64, blank=False, unique=True)

that uses the Django default primary key. I want users to be able to add products by setting the third_party_id on the add page, but I don't want that field editable in the edit page to avoid corrupting the third_party_id. In the Django docs, the same settings appear to be used for add and edit. Is this possible?

Phil Loden
  • 1,394
  • 1
  • 15
  • 15

3 Answers3

65

Do not set self.readonly_fields to avoid thread issues. Instead override get_readonly_fields method:

def get_readonly_fields(self, request, obj=None):
    if obj: # obj is not None, so this is an edit
        return ['third_party_id',] # Return a list or tuple of readonly fields' names
    else: # This is an addition
        return []
shanyu
  • 9,536
  • 7
  • 60
  • 68
  • 3
    Very cool didn't know about this method. +1. What can we do if there isn't a method for overriding? Any strategies? Much appreciated. – Yuji 'Tomita' Tomita Oct 23 '11 at 08:35
  • 1
    @YUji, ModelAdmin's get_form and get_formset methods cover almost every use case. You may need to have a look at the code (options.py), those methods were not included in the docs at the time I looked for a solution for this topic. – shanyu Oct 23 '11 at 16:40
  • I just meant in general python if there wasn't a convenient method to override. Just a lock.acquire()? – Yuji 'Tomita' Tomita Oct 23 '11 at 21:14
  • Python provides concurrency through threading and multiprocessing modules, you can look at the relevant docs. However I really doubt they should be put to use in a situation as trivial as admin customization. – shanyu Oct 24 '11 at 15:15
  • Hey Shanyu, I'll take a look at them. I wasn't suggesting its use here since there's such a nice hook you revealed, but I've run into similar situations in the past modifying a class attribute and wanted your opinion on the matter. I really appreciate you answering. – Yuji 'Tomita' Tomita Oct 24 '11 at 19:57
  • The official document is [here](https://docs.djangoproject.com/en/2.0/ref/contrib/admin/#django.contrib.admin.ModelAdmin.get_readonly_fields). – ramwin Apr 26 '18 at 05:28
  • 2
    Would be worth mentionning this code must go into ModelAdmin not Model itself – Overdrivr Dec 16 '20 at 13:57
5

The above is helpful (shanyu's answer using get_readonly_fields), however it does not work properly if used in "StackedInline". The result is two copies of whatever field is marked readonly, and it is not editable in the "add" instance. See this bug: https://code.djangoproject.com/ticket/15602

Hope this saves someone some searching!

Jenni
  • 51
  • 1
1

I am not sure if this is the best way, but you could define your own form for the admin. And custom validate your third_party_id, rejecting if it is already set:

Admin.py

class ProductAdminForm(forms.ModelForm):
    class Meta:
        model = Product

    def clean_third_party_id(self):
        cleaned_data = self.cleaned_data
        third_party_id = cleaned_data['third_party_id']
        id = cleaned_data['id']
        obj = Product.objects.get(id=id)
        if obj.third_party_id != third_party_id:
            raise ValidationError("You cannot edit third_party_id, it must stay as %s" % obj.third_party_id)
         return third_party_id


class ProductAdmin(admin.Admin):
    form = [ProductAdminForm,]
Narsilou
  • 321
  • 1
  • 2
  • 6