2

I am trying to figure this one out and for some reason every attempt failed so far. I have two simple models: Question and Answer:

class Question(models.Model):
    phoneID = models.CharField(max_length=255, editable=False)
    name = models.CharField(max_length=255, editable=False)
    phone = models.CharField(max_length=255, editable=False)
    message = models.TextField(editable=False)
    answered = models.DateTimeField(editable=False)
    created = models.DateTimeField(auto_now_add=True, default=datetime.utcnow())

class Answer(models.Model):
    question = models.ForeignKey(Question)
    message = models.TextField()
    created = models.DateTimeField(auto_now_add=True, default=datetime.utcnow())

For some reason I am unable to figure out what I am supposed to put into my admin.py to have previously added answers listed inline as readonly and at the same time allow inline new answers to be added. Everytime I set readonly_fields=('message') I am not able to add a new answer because the message textarea is readonly.

janbrot
  • 175
  • 2
  • 11
  • Possible duplicate of [Readonly for existing items only in Django admin inline](https://stackoverflow.com/questions/5619120/readonly-for-existing-items-only-in-django-admin-inline) – Shahar 'Dawn' Or Apr 15 '18 at 19:30

3 Answers3

5

Ok, I understand what you want. To do so you can create a special widget which will show field value and at the same time will be a hidden input. Use this widget for previous answers:

from django.forms.widgets import Widget
from django.forms.utils import flatatt
from django.utils.html import format_html

class ReadOnlyInput(Widget):

    def render(self, name, value, attrs=None):
        if value is None:
            value = ''
        final_attrs = self.build_attrs(attrs, type='hidden',
                                       name=name, value=value)
        return format_html('<input{} />{}', flatatt(final_attrs), value)


class AnswerForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        super(AnswerForm, self).__init__(*args, **kwargs)
        if self.instance.pk:
            self.fields['message'].widget = ReadOnlyInput()


class AnswerInline(admin.TabularInline):
    form = AnswerForm
    model = Answer
catavaran
  • 44,703
  • 8
  • 98
  • 85
  • Actually the post here like I mentioned before (http://stackoverflow.com/questions/18269366/making-readonly-for-existing-field-but-allowing-to-add-when-new-inline-is-create) works totally fine. What is the difference with your code? Is your solution more efficient? I am just asking, since I am still getting to fully understand the framework. Thx – janbrot Jan 17 '15 at 00:17
  • 1
    My solution is straightforward, bulletproof and uses no magic. This is the way django forms work. The mentioned answer uses `_construct_form` which is private method of `BaseInlineFormSet` (methods started with underscore are private and shouldn't be used). – catavaran Jan 17 '15 at 00:40
  • Good answer. Thanks – Sunny Chaudhari Nov 09 '17 at 11:25
  • You could also do `self.fields[field_name].disabled = True` without creating a new widget. – Arman Ordookhani Jul 16 '19 at 15:33
  • @ArmanOrdookhani if you use `disabled = True` the selector is still disabled when you add a new one though. – Nick BL May 14 '20 at 14:42
  • @NickBL Did you put it in `if self.instance.pk:`? I'm not sure after some years but I think it'll work. – Arman Ordookhani May 15 '20 at 11:22
  • @ArmanOrdookhani Oh i misunderstood your pointer, you meant to add that line instead of the ...widget = ReadOnlyInput(), I follow, good idea – Nick BL May 21 '20 at 19:14
0

Django 1.7 allows you to display object's property in admin inline.

class AnswerInline(admin.TabularInline):
    model = Answer
    fields = ('message', 'message_display', )
    readonly_fields=('message_display', )

    def message_display(self, obj):
        return obj.message
catavaran
  • 44,703
  • 8
  • 98
  • 85
  • Thank you catavaran, I actually learned something new. Unfortunately it still does not do what I want. Maybe I am not making myself clear enough. At the end I have a question and several answers displaying inline. I want all already existing answers to be readonly (not editable). And on top of that I want to be able to add a new answer to the inline form. But exactly the last step does not work. It is either or. If I make the old answers readonly and try to add a new answer, the fields are also only readonly and due to that i cannot add a new answer inline. – janbrot Jan 15 '15 at 14:18
  • Actually this person had the same issue Look at this question http://stackoverflow.com/questions/18269366/making-readonly-for-existing-field-but-allowing-to-add-when-new-inline-is-create . But the solution he proposed himself, did not work for me. – janbrot Jan 15 '15 at 14:19
0

Was having same question today. First was trying to modify form, then get_readonly_fields, then many other things. Then remembered this elegant thing:

class MyInline(admin.StackedInline):
    def has_change_permission(self, request, obj=None):
        return False
LinPy fan
  • 179
  • 1
  • 11