我可以在Django ModelForm中覆盖__new__吗?

时间:2019-07-10 14:59:01

标签: python django django-forms modelform

我在Django 1.11上有一个ModelForm,并且正在将一些字段移至另一个模型。调用make_migrations会导致错误,因为这些字段在当前模型中不存在。我在表单中添加了一些字段,但是其中一个字段是TranslatedField,因此当前有2个字段,将来可能会更多,具体取决于语言的数量。该字段的名称为city,目前我收到一条错误消息“ Unknown field(s) (city_en, city_he) specified for SiteProfile”(因为我使用的是“ en”和“ he”这两种语言),但我想使用以下方式动态创建所有字段for循环遍历我们在项目中使用的语言。我可以覆盖__new__方法(这是一种很好的编程方法)还是其他方法?我不希望对特定的字段名称(city_encity_he)进行硬编码,因为它们将来可能会更改,具体取决于我们使用的语言种类。

您可以在GitHub上看到我当前的commit(无效)。

以及该分支的当前code

我想知道什么是定义动态字段列表的最佳编程方法(所有字段都相同,将只使用其中一个,而其他__init__方法中将其删除)一个ModelForm,其中的字段保存在另一个模型中(有2个模型,但只有一个表单)。

由于运行make_migrations时出现此错误,我仍然没有提交迁移。

(我定义了一个命令make_migrations,它仅执行makemigrations

表单(我尝试覆盖__new__):

class SpeedyMatchProfileBaseForm(DeleteUnneededFieldsMixin, forms.ModelForm):
    user_fields = (
        'diet',
        'smoking_status',
        'marital_status',
        *(to_attribute(name='city', language_code=language_code) for language_code, language_name in django_settings.LANGUAGES),
    )
    validators = {
        'height': [speedy_match_accounts_validators.validate_height],
        'diet': [speedy_match_accounts_validators.validate_diet],
        'smoking_status': [speedy_match_accounts_validators.validate_smoking_status],
        'marital_status': [speedy_match_accounts_validators.validate_marital_status],
        **{to_attribute(name='profile_description', language_code=language_code): [speedy_match_accounts_validators.validate_profile_description] for language_code, language_name in django_settings.LANGUAGES},
        **{to_attribute(name='city', language_code=language_code): [speedy_match_accounts_validators.validate_city] for language_code, language_name in django_settings.LANGUAGES},
        **{to_attribute(name='children', language_code=language_code): [speedy_match_accounts_validators.validate_children] for language_code, language_name in django_settings.LANGUAGES},
        **{to_attribute(name='more_children', language_code=language_code): [speedy_match_accounts_validators.validate_more_children] for language_code, language_name in django_settings.LANGUAGES},
        **{to_attribute(name='match_description', language_code=language_code): [speedy_match_accounts_validators.validate_match_description] for language_code, language_name in django_settings.LANGUAGES},
        'gender_to_match': [speedy_match_accounts_validators.validate_gender_to_match],
        'min_age_match': [speedy_match_accounts_validators.validate_min_age_match],
        'max_age_match': [speedy_match_accounts_validators.validate_max_age_match],
        'diet_match': [speedy_match_accounts_validators.validate_diet_match],
        'smoking_status_match': [speedy_match_accounts_validators.validate_smoking_status_match],
        'marital_status_match': [speedy_match_accounts_validators.validate_marital_status_match],
    }
    # ~~~~ TODO: diet choices depend on the current user's gender. Also same for smoking status and marital status.
    diet = forms.ChoiceField(choices=User.DIET_VALID_CHOICES, widget=forms.RadioSelect(), label=_('My diet'))
    smoking_status = forms.ChoiceField(choices=User.SMOKING_STATUS_VALID_CHOICES, widget=forms.RadioSelect(), label=_('My smoking status'))
    marital_status = forms.ChoiceField(choices=User.MARITAL_STATUS_VALID_CHOICES, widget=forms.RadioSelect(), label=_('My marital status'))
    photo = forms.ImageField(required=False, widget=CustomPhotoWidget, label=_('Add profile picture'))

    class Meta:
        model = SpeedyMatchSiteProfile
        fields = (
            'photo',
            *(to_attribute(name='profile_description', language_code=language_code) for language_code, language_name in django_settings.LANGUAGES),
            *(to_attribute(name='city', language_code=language_code) for language_code, language_name in django_settings.LANGUAGES),
            'height',
            *(to_attribute(name='children', language_code=language_code) for language_code, language_name in django_settings.LANGUAGES),
            *(to_attribute(name='more_children', language_code=language_code) for language_code, language_name in django_settings.LANGUAGES),
            'diet',
            'smoking_status',
            'marital_status',
            'gender_to_match',
            *(to_attribute(name='match_description', language_code=language_code) for language_code, language_name in django_settings.LANGUAGES),
            'min_age_match',
            'max_age_match',
            'diet_match',
            'smoking_status_match',
            'marital_status_match',
        )
        widgets = {
            'smoking_status': forms.RadioSelect(),
            'marital_status': forms.RadioSelect(),
            **{to_attribute(name='profile_description', language_code=language_code): forms.Textarea(attrs={'rows': 3, 'cols': 25}) for language_code, language_name in django_settings.LANGUAGES},
            **{to_attribute(name='city', language_code=language_code): forms.TextInput() for language_code, language_name in django_settings.LANGUAGES},
            **{to_attribute(name='children', language_code=language_code): forms.TextInput() for language_code, language_name in django_settings.LANGUAGES},
            **{to_attribute(name='more_children', language_code=language_code): forms.TextInput() for language_code, language_name in django_settings.LANGUAGES},
            **{to_attribute(name='match_description', language_code=language_code): forms.Textarea(attrs={'rows': 3, 'cols': 25}) for language_code, language_name in django_settings.LANGUAGES},
            'diet_match': CustomJsonWidget(choices=User.DIET_VALID_CHOICES),
            'smoking_status_match': CustomJsonWidget(choices=User.SMOKING_STATUS_VALID_CHOICES),
            'marital_status_match': CustomJsonWidget(choices=User.MARITAL_STATUS_VALID_CHOICES),
        }

    @staticmethod
    def __new__(cls, *args, **kwargs):
        for language_code, language_name in django_settings.LANGUAGES:
            setattr(cls, to_attribute(name='city', language_code=language_code), forms.CharField(label=_('city or locality'), max_length=120))
        return super().__new__(*args, **kwargs)

    def __init__(self, *args, **kwargs):
        self.step = kwargs.pop('step', None)
        super().__init__(*args, **kwargs)
        self.delete_unneeded_fields()
        if ('gender_to_match' in self.fields):
            self.fields['gender_to_match'] = forms.MultipleChoiceField(choices=User.GENDER_CHOICES, widget=forms.CheckboxSelectMultiple)
        if ('photo' in self.fields):
            self.fields['photo'].widget.attrs['user'] = self.instance.user
        if ('diet' in self.fields):
            update_form_field_choices(field=self.fields['diet'], choices=self.instance.user.get_diet_choices())
            self.fields['diet'].initial = self.instance.user.diet
        if ('smoking_status' in self.fields):
            update_form_field_choices(field=self.fields['smoking_status'], choices=self.instance.user.get_smoking_status_choices())
            self.fields['smoking_status'].initial = self.instance.user.smoking_status
        if ('marital_status' in self.fields):
            update_form_field_choices(field=self.fields['marital_status'], choices=self.instance.user.get_marital_status_choices())
            self.fields['marital_status'].initial = self.instance.user.marital_status
        if ('diet_match' in self.fields):
            update_form_field_choices(field=self.fields['diet_match'], choices=self.instance.get_diet_match_choices())
        if ('smoking_status_match' in self.fields):
            update_form_field_choices(field=self.fields['smoking_status_match'], choices=self.instance.get_smoking_status_match_choices())
        if ('marital_status_match' in self.fields):
            update_form_field_choices(field=self.fields['marital_status_match'], choices=self.instance.get_marital_status_match_choices())
        for field_name, field in self.fields.items():
            if (field_name in self.validators):
                field.validators.extend(self.validators[field_name])
                field.required = True

4 个答案:

答案 0 :(得分:1)

  

更新2: ... ...但是,该字段被创建为表单中的最后一个字段,我希望它位于中间。有没有办法在中间添加此字段?

来自https://docs.djangoproject.com/en/2.1/ref/forms/api/#notes-on-field-ordering

  

如果Error: Package: glibc-2.12-1.212.el6_10.3.i686 (updates) Requires: glibc-common = 2.12-1.212.el6_10.3 Installed: glibc-common-2.17-55.el6.x86_64 (installed) glibc-common = 2.17-55.el6 Available: glibc-common-2.12-1.80.el6.x86_64 (centos6u3) glibc-common = 2.12-1.80.el6 Available: glibc-common-2.12-1.212.el6.x86_64 (base) glibc-common = 2.12-1.212.el6 Available: glibc-common-2.12-1.212.el6_10.3.x86_64 (updates) glibc-common = 2.12-1.212.el6_10.3 You could try using --skip-broken to work around the problem ** Found 2 pre-existing rpmdb problem(s), 'yum check' output follows: glibc-2.12-1.209.el6_9.2.i686 has missing requires of glibc-common = ('0', '2.12', '1.209.el6_9.2') glibc-2.17-55.el6.x86_64 is a duplicate with glibc-2.12-1.209.el6_9.2.i686 是字段名称的列表,则按照列表指定的顺序对字段进行排序,并根据默认顺序附加其余字段。 ...

     

您可以随时使用field_orderorder_fields()中的字段名称列表重新排列字段。

field_order

答案 1 :(得分:0)

我不知道这是否有帮助,但是我有一个调查应用程序,其中不同的用户希望在背景信息表上获得不同的信息。我通过json文件提供。因此,在表单中,我有

[{
    "fieldset" : "Basic Information",
    "fields" : [
        {
            "field" : "form_filler",
            "div" : ["form_filler_other"]
        },{
            "field" : "child_dob"
        },{
            "field" : "age"
        },{
            "field" : "sex"
        },{
            "field" : "country", 
            "div" : ["zip_code"]
        },{
            "field" : "birth_order"
        }, {
            "field" : "multi_birth_boolean",
            "div" : ["multi_birth"]
        }, {
            "field" : "birth_weight_kg",
            "choices" : [
                {
                    "key" : "",
                    "value" : "--------"
                },{ 
                    "key" : "1.0",
                    "value" : "Henry 1"
                },{ 
                    "key" : "2.0",
                    "value" : "Weight 2"
                },{ 
                    "key" : "3.0",
                    "value" : "Weight 3"
                },{ 
                    "key" : "4.0",
                    "value" : "Weight 4"
                }
            ],
            "widget_type" :  "Select"
        }, {
            "field" : "born_on_due_date", 
            "div" : ["early_or_late", "due_date_diff"]
        }

    ]
}, { ...
}]

我的json文件如下所示:

field

div键是一个输入字段,div列表中的项目也是如此。在这种情况下,当选择字段(BooleanField)时,{{1}}列表中的字段必须完成,这是我通过clean方法所做的。

我的所有字段都在模型中指定,因此您只能选择要使用的字段

答案 2 :(得分:0)

真的很难理解您到底有什么问题。如果您可以回答我在下面提出的问题,那将非常有帮助。

  

我正在将几个字段移至另一个模型

哪些字段?从哪个型号?要哪个型号? 我假设您将city字段从User模型移到SiteProfile

  

调用make_migrations会导致错误,因为这些字段没有   当前模型中存在

什么错误? current model指的是什么? SiteProfile?将字段从一种模型转移到另一种模型应该是完全可行的。

我环顾了您的存储库。特别是您尝试从django-modeltranslation迁移到django-translated-fields的分支。并且还在Github上的django-translated-fields存储库中找到了您的问题。

很遗憾,我无法完全理解您遇到的问题。 我认为您的问题可以分为两个独立的问题。

  1. 迁移不适用于django-translated-fields
  2. 动态创建表单中的已翻译字段。

所以也许我们可以从迁移开始。 当您说迁移不起作用时,您是什么意思。你能告诉我这些错误吗?

答案 3 :(得分:0)

如果您希望每次更改模型时都动态地在ModelForm中显示模型的所有字段,则可以进行检查。 (我已经尝试过,它可以工作。)

forms.py

created_at = models.DateTimeField(auto_now_add=True)

django.core.exceptions.FieldError: 'created_at' cannot be specified for Episode model form as it is a non-editable field

-----大警告-------

它将显示不可编辑字段的错误,例如(如果您可以更改功能以排除这些类型的字段,请编辑答案。):

auto_now_add=True

如果您从其中删除import SwiftUI import UIKit import AVFoundation class PreviewView: UIView { private var captureSession: AVCaptureSession? init() { super.init(frame: .zero) var allowedAccess = false let blocker = DispatchGroup() blocker.enter() AVCaptureDevice.requestAccess(for: .video) { flag in allowedAccess = flag blocker.leave() } blocker.wait() if !allowedAccess { print("!!! NO ACCESS TO CAMERA") return } // setup session let session = AVCaptureSession() session.beginConfiguration() let videoDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .unspecified) //alternate AVCaptureDevice.default(for: .video) guard videoDevice != nil, let videoDeviceInput = try? AVCaptureDeviceInput(device: videoDevice!), session.canAddInput(videoDeviceInput) else { print("!!! NO CAMERA DETECTED") return } session.addInput(videoDeviceInput) session.commitConfiguration() self.captureSession = session } override class var layerClass: AnyClass { AVCaptureVideoPreviewLayer.self } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } var videoPreviewLayer: AVCaptureVideoPreviewLayer { return layer as! AVCaptureVideoPreviewLayer } override func didMoveToSuperview() { super.didMoveToSuperview() if nil != self.superview { self.videoPreviewLayer.session = self.captureSession self.videoPreviewLayer.videoGravity = .resizeAspect self.captureSession?.startRunning() } else { self.captureSession?.stopRunning() } } } struct PreviewHolder: UIViewRepresentable { func makeUIView(context: UIViewRepresentableContext<PreviewHolder>) -> PreviewView { PreviewView() } func updateUIView(_ uiView: PreviewView, context: UIViewRepresentableContext<PreviewHolder>) { } typealias UIViewType = PreviewView } struct DemoVideoStreaming: View { var body: some View { VStack { PreviewHolder() }.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center) } } ,它将起作用。

注意

它将为所有选项的ForiegnKeyField创建下拉列表。