[django] Django 양식에서 편집 할 수 없도록 필드를 읽기 전용 (또는 비활성화)으로 만들려면 어떻게합니까?

Django 양식에서 필드를 읽기 전용 (또는 비활성화)으로 만들려면 어떻게합니까?

양식을 사용하여 새 항목을 작성하는 경우 모든 필드를 사용해야하지만 레코드가 업데이트 모드 인 경우 일부 필드는 읽기 전용이어야합니다.

예를 들어, 새 Item모델을 만들 때 모든 필드를 편집 할 수 있어야하지만 레코드를 업데이트하는 동안 sku필드를 볼 수는 있지만 편집 할 수 없도록 필드 를 비활성화하는 방법이 있습니까?

class Item(models.Model):
    sku = models.CharField(max_length=50)
    description = models.CharField(max_length=200)
    added_by = models.ForeignKey(User)


class ItemForm(ModelForm):
    class Meta:
        model = Item
        exclude = ('added_by')

def new_item_view(request):
    if request.method == 'POST':
        form = ItemForm(request.POST)
        # Validate and save
    else:
            form = ItemForm()
    # Render the view

수업 ItemForm을 재사용 할 수 있습니까 ? ItemForm또는 Item모델 클래스 에서 어떤 변경이 필요 합니까? ItemUpdateForm항목을 업데이트하기 위해 다른 클래스 ” ” 를 작성해야 합니까?

def update_item_view(request):
    if request.method == 'POST':
        form = ItemUpdateForm(request.POST)
        # Validate and save
    else:
        form = ItemUpdateForm()



답변

이 답변 에서 지적했듯이 Django 1.9는 Field.disabled 속성을 추가했습니다 .

비활성화 된 부울 인수는 True로 설정되면 비활성화 된 HTML 속성을 사용하여 양식 필드를 비활성화하여 사용자가 편집 할 수 없도록합니다. 사용자가 서버에 제출 된 필드 값을 변경하더라도 양식의 초기 데이터 값을 위해 무시됩니다.

Django 1.8 및 이전 버전에서는 위젯에서 항목을 비활성화하고 악의적 인 POST 해킹을 방지 readonly하려면 양식 필드 에서 속성 을 설정하고 입력을 제거해야 합니다.

class ItemForm(ModelForm):
    def __init__(self, *args, **kwargs):
        super(ItemForm, self).__init__(*args, **kwargs)
        instance = getattr(self, 'instance', None)
        if instance and instance.pk:
            self.fields['sku'].widget.attrs['readonly'] = True

    def clean_sku(self):
        instance = getattr(self, 'instance', None)
        if instance and instance.pk:
            return instance.sku
        else:
            return self.cleaned_data['sku']

또는 if instance and instance.pk편집 중임을 나타내는 다른 조건으로 바꾸 십시오. disabled대신 입력 필드 에서 속성 을 설정할 수도 있습니다 readonly.

clean_sku함수는 readonly값이에 의해 재정의되지 않도록합니다 POST.

그렇지 않으면 바운드 입력 데이터를 거부하면서 값을 렌더링하는 내장 장고 양식 필드가 없습니다. 원하는 ModelForm경우 편집 할 수없는 필드를 제외한 별도의 필드 를 만들어 템플릿 내부에 인쇄하면됩니다.


답변

Django 1.9는 Field.disabled 속성을 추가했습니다 : https://docs.djangoproject.com/en/stable/ref/forms/fields/#disabled

비활성화 된 부울 인수는 True로 설정되면 비활성화 된 HTML 속성을 사용하여 양식 필드를 비활성화하여 사용자가 편집 할 수 없도록합니다. 사용자가 서버에 제출 된 필드 값을 변경하더라도 양식의 초기 데이터 값을 위해 무시됩니다.


답변

readonly위젯을 설정 하면 브라우저의 입력 만 읽기 전용이됩니다. 추가 clean_sku하는 반환하는 instance.sku필드 값이 폼 수준에서 변경되지 않습니다 보장합니다.

def clean_sku(self):
    if self.instance:
        return self.instance.sku
    else:
        return self.fields['sku']

이렇게하면 모델 (수정되지 않은 저장)을 사용하고 필드 필수 오류가 발생하지 않도록 할 수 있습니다.


답변

아 워커의 대답 이 많은 도움이되었습니다!

get_readonly_fields 사용하여 장고 1.3에서 작동하도록 그의 예제를 변경했습니다 .

일반적으로 다음과 같이 선언해야합니다 app/admin.py.

class ItemAdmin(admin.ModelAdmin):
    ...
    readonly_fields = ('url',)

나는 이런 식으로 적응했다.

# In the admin.py file
class ItemAdmin(admin.ModelAdmin):
    ...
    def get_readonly_fields(self, request, obj=None):
        if obj:
            return ['url']
        else:
            return []

그리고 잘 작동합니다. 이제 항목을 추가하면 url필드가 읽기 / 쓰기가되지만 변경하면 읽기 전용이됩니다.


답변

ForeignKey필드 에서이 작업을 수행하려면 몇 가지 사항을 변경해야합니다. 먼저, SELECT HTML태그에는 읽기 전용 속성이 없습니다. disabled="disabled"대신 사용해야 합니다. 그러나 브라우저는 해당 필드에 대한 양식 데이터를 다시 보내지 않습니다. 따라서 필드의 유효성이 올바르게 검사되도록 해당 필드가 필요하지 않도록 설정해야합니다. 그런 다음 값을 공백으로 설정하지 않도록 원래 값으로 다시 설정해야합니다.

따라서 외래 키의 경우 다음과 같은 작업을 수행해야합니다.

class ItemForm(ModelForm):

    def __init__(self, *args, **kwargs):
        super(ItemForm, self).__init__(*args, **kwargs)
        instance = getattr(self, 'instance', None)
        if instance and instance.id:
            self.fields['sku'].required = False
            self.fields['sku'].widget.attrs['disabled'] = 'disabled'

    def clean_sku(self):
        # As shown in the above answer.
        instance = getattr(self, 'instance', None)
        if instance:
            return instance.sku
        else:
            return self.cleaned_data.get('sku', None)

이렇게하면 브라우저에서 사용자가 필드를 변경할 수 없으며 항상 POST비워 둔 상태로 유지됩니다. 그런 다음 clean필드의 값을 인스턴스의 원래 값으로 설정하기 위해 메서드를 재정의합니다 .


답변

Django 1.2 이상에서는 다음과 같이 필드를 무시할 수 있습니다.

sku = forms.CharField(widget = forms.TextInput(attrs={'readonly':'readonly'}))


답변

상속되지 않은 MixIn 클래스를 작성하여 첫 번째 편집에서 필드를 비활성화하고 보호하는 read_only iterable 필드를 추가 할 수 있습니다.

(다니엘과 Muhuk의 답변을 바탕으로 함)

from django import forms
from django.db.models.manager import Manager

# I used this instead of lambda expression after scope problems
def _get_cleaner(form, field):
    def clean_field():
         value = getattr(form.instance, field, None)
         if issubclass(type(value), Manager):
             value = value.all()
         return value
    return clean_field

class ROFormMixin(forms.BaseForm):
    def __init__(self, *args, **kwargs):
        super(ROFormMixin, self).__init__(*args, **kwargs)
        if hasattr(self, "read_only"):
            if self.instance and self.instance.pk:
                for field in self.read_only:
                    self.fields[field].widget.attrs['readonly'] = "readonly"
                    setattr(self, "clean_" + field, _get_cleaner(self, field))

# Basic usage
class TestForm(AModelForm, ROFormMixin):
    read_only = ('sku', 'an_other_field')