django admin - добавить настраиваемые поля формы, не являющиеся частью модели

108

У меня модель зарегистрирована в админке. Одно из его полей - длинное строковое выражение. Я хотел бы добавить настраиваемые поля формы на страницу добавления / обновления этой модели в админке, чтобы на основе значений этих полей я построил длинное строковое выражение и сохраню его в соответствующем поле модели.

Как я могу это сделать?

ОБНОВЛЕНИЕ: в основном я строю математическое или строковое выражение из символов, пользователь выбирает символы (это настраиваемые поля, которые не являются частью модели), и когда он нажимает кнопку «Сохранить», я создаю представление строкового выражения из список символов и сохранить его в БД. Я не хочу, чтобы символы были частью модели и БД, только окончательное выражение.

michalv82
источник

Ответы:

156

Либо в вашем admin.py, либо в отдельном forms.py вы можете добавить класс ModelForm, а затем объявить свои дополнительные поля внутри него, как обычно. Я также привел пример того, как вы можете использовать эти значения в form.save ():

from django import forms
from yourapp.models import YourModel


class YourModelForm(forms.ModelForm):

    extra_field = forms.CharField()

    def save(self, commit=True):
        extra_field = self.cleaned_data.get('extra_field', None)
        # ...do something with extra_field here...
        return super(YourModelForm, self).save(commit=commit)

    class Meta:
        model = YourModel

Чтобы дополнительные поля отображались в админке, просто:

  1. отредактируйте свой admin.py и установите свойство формы, чтобы оно ссылалось на форму, которую вы создали выше
  2. включить ваши новые поля в свои поля или объявления fieldsets

Как это:

class YourModelAdmin(admin.ModelAdmin):

    form = YourModelForm

    fieldsets = (
        (None, {
            'fields': ('name', 'description', 'extra_field',),
        }),
    )

ОБНОВЛЕНИЕ: в django 1.8 вам нужно добавить fields = '__all__'в метакласс YourModelForm.

Вишну
источник
2
Да, я действительно хочу знать причину
Вишну
14
Вы должны добавить fields = '__all__'в свой Metaкласс, иначе django жалуется:Creating a ModelForm without either the 'fields' attribute or the 'exclude' attribute is deprecated
IsaacKleiner 01
1
@sthzg, потому что это не правильно. Это дает мне ошибку:YourModelAdmin.list_display[0], 'extra_field' is not a callable or an attribute of 'YourModelAdmin' or found in the model 'YourModel'.
Cerin
7
Если по какой-то причине вы получили AttributeError: Unable to lookup "extra_field"..., попробуйте добавить labelк extra_fieldопределению. Похоже, что django пытается «угадать» метку для него, глядя на Modelи ModelAdminдля определения такого атрибута.
alxs
1
Это прекрасно работало, если extra_field - это CharField (). Если это hiddenField [в Django 1.11], генерируется ошибка Unknown field(s) (extra_field) specified for YourModel. Check fields/fieldsets/exclude attributes of class YourModelAdmin.. Обходной путь для этогоextra_field = forms.CharField(widget=forms.HiddenInput())
kakoma 04
36

Это можно сделать в админке, но это не очень простой способ. Кроме того, я хотел бы посоветовать сохранить большую часть бизнес-логики в своих моделях, чтобы вы не зависели от администратора Django.

Может быть, было бы проще (а может быть, даже лучше), если бы в вашей модели было два отдельных поля. Затем добавьте к своей модели метод, который их объединяет.

Например:

class MyModel(models.model):

    field1 = models.CharField(max_length=10)
    field2 = models.CharField(max_length=10)

    def combined_fields(self):
        return '{} {}'.format(self.field1, self.field2)

Затем в админке вы можете добавить поле combined_fields()только для чтения:

class MyModelAdmin(models.ModelAdmin):

    list_display = ('field1', 'field2', 'combined_fields')
    readonly_fields = ('combined_fields',)

    def combined_fields(self, obj):
        return obj.combined_fields()

Если вы хотите сохранить combined_fieldsв базе данных, вы также можете сохранить его при сохранении модели:

def save(self, *args, **kwargs):
    self.field3 = self.combined_fields()
    super(MyModel, self).save(*args, **kwargs)
гитаарик
источник
4
Спасибо за ответ, но я не это ищу. Я не хочу, чтобы настраиваемые поля сохранялись в БД, только вычисляемая строка. В основном я строю математическое или строковое выражение из символов, пользователь выбирает символы (это настраиваемые поля, которые не являются частью модели), и когда он нажимает кнопку «Сохранить», я создаю представление строкового выражения из списка символы и сохранить в БД.
michalv82
@ michalv82 Так же можно сохранить в базе в save()методе модели , проверяйте обновления моего ответа.
gitaarik
1
еще раз спасибо, но проблема в том, что я не хочу хранить поля, которые объединяют последнее поле (т.е. символы), я просто хочу, чтобы последняя строка была сохранена
michalv82
Есть проблема сохранить 2 поля? Может быть, это пригодится, если вы хотите знать, как было создано комбинированное поле.
gitaarik
6
еще раз спасибо но это не 2 поля, наверное будет больше. Опять же, я НЕ хочу хранить их в БД, поэтому это решение не сработает для меня.
michalv82
6

Django 2.1.1 Первый ответ заставил меня наполовину ответить на мой вопрос. Это не помогло мне сохранить результат в поле моей реальной модели. В моем случае мне нужно текстовое поле, в которое пользователь может вводить данные, а затем, когда произойдет сохранение, данные будут обработаны, а результат будет помещен в поле модели и сохранен. В то время как исходный ответ показал, как получить значение из дополнительного поля, он не показал, как сохранить его обратно в модель, по крайней мере, в Django 2.1.1.

Это берет значение из несвязанного настраиваемого поля, обрабатывает и сохраняет его в моем реальном поле описания:

class WidgetForm(forms.ModelForm):
    extra_field = forms.CharField(required=False)

    def processData(self, input):
        # example of error handling
        if False:
            raise forms.ValidationError('Processing failed!')

        return input + " has been processed"

    def save(self, commit=True):
        extra_field = self.cleaned_data.get('extra_field', None)

        # self.description = "my result" note that this does not work

        # Get the form instance so I can write to its fields
        instance = super(WidgetForm, self).save(commit=commit)

        # this writes the processed data to the description field
        instance.description = self.processData(extra_field)

        if commit:
            instance.save()

        return instance

    class Meta:
        model = Widget
        fields = "__all__"
MangoLassi
источник
4

вы всегда можете создать новый шаблон администратора и делать то, что вам нужно, в своем admin_view (переопределите URL-адрес администратора, добавив его в свой admin_view):

 url(r'^admin/mymodel/mymodel/add/$' , 'admin_views.add_my_special_model')
Эяль Ч
источник
3

Если вы хотите сохранить в модели только объединенное поле, а не два отдельных поля, вы можете сделать что-то вроде этого:

Я никогда не делал ничего подобного, поэтому не совсем уверен, как это сработает.

гитаарик
источник