【问题标题】:Django get_or_create only if form constraints are metDjango get_or_create 仅在满足表单约束时
【发布时间】:2016-10-08 19:42:29
【问题描述】:

我有一个表格,要求输入歌曲的艺术家、标题和混音。艺术家和标题是必填字段,但混合不是。仅当 Artist、Title 和 Mix 不存在时,才应保存该表单。如果表单有空的艺术家或标题字段,它应该在提交时显示“此字段是必需的”。我遇到的问题是,如果 Title 字段为空但 Artist 已填充,它仍会使用 get_or_create 创建 Artist 对象(请参阅下面的###forms.py)。如果表单有效,如何仅创建 Artist 对象?

###########models.py
class Artist (models.Model):
    name = models.CharField(max_length=100)

class Track (models.Model):    
    artist = models.ForeignKey(Artist, blank=True, null=True, on_delete=models.SET_NULL, verbose_name="Artist")
    user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, on_delete=models.SET_NULL, verbose_name="Submitted by", default=1)
    title = models.CharField(max_length=100, verbose_name="Title")
    mix = models.CharField(max_length=100, blank=True, verbose_name="Mix")

###########views.py
class TrackCreateView(SuccessMessageMixin, AjaxCreateView):
    form_class = ProfileForm
    success_message = "Thank you for submitting track: %(artist)s - %(title)s - %(mix)s"

    def get_initial(self):
        self.initial.update({ 'user': self.request.user })
        return self.initial

    def get_success_message(self, cleaned_data):
        return self.success_message % dict(cleaned_data, 
            artist=self.object.artist, 
            title=self.object.title,
        )

###########forms.py
class ProfileForm(forms.ModelForm):

    class Meta:
        model = Track
        fields = [
            "artist",
            "title",
            "mix",
            ]        
    artist = forms.CharField(widget=forms.TextInput(attrs={'maxlength': '100',}))        

    def __init__(self, *args, **kwargs):
        self.user = kwargs['initial']['user']
        super(ProfileForm, self).__init__(*args, **kwargs)
        # Set layout for fields.
        my_field_text= [
            ('artist', 'Artist', ''),
            ('title', 'Title', ''),
            ('mix', 'Mix', ''),
        ]
        for x in my_field_text:
            self.fields[x[0]].label=x[1]
            self.fields[x[0]].help_text=x[2]

        self.helper = FormHelper()
        self.helper.layout = Layout(
            Div(
                Div('artist', css_class="col-sm-4"),
                Div('title', css_class="col-sm-4"),
                Div('mix', css_class="col-sm-4"),               
                css_class = 'row'
            ),
        )


    def save(self, commit=True):
        obj = super(ProfileForm, self).save(False)
        obj.user = self.user
        commit and obj.save()
        return obj

    def clean(self):
        cleaned_data = super(ProfileForm, self).clean()

        artist = self.cleaned_data.get('artist')
        title = self.cleaned_data.get('title')
        mix = self.cleaned_data.get('mix')

        if artist and title:
            title = ' '.join([w.title() if w.islower() else w for w in title.split()])
            if mix:
                mix = ' '.join([w.title() if w.islower() else w for w in mix.split()])

            if Track.objects.filter(artist=artist, title=title, mix=mix).exists():
                msg = "Record with Artist and Title already exists."
                if mix:
                    msg = "Record with Artist, Title & Mix already exists."
                    self.add_error('mix', msg)
                self.add_error('artist', msg)
                self.add_error('title', msg)

        if not artist:
            raise forms.ValidationError("Artist is a required field.")
        else:
            artist, created = Artist.objects.get_or_create(name=artist)
            self.cleaned_data['artist'] = artist


        self.cleaned_data['title'] = title 
        self.cleaned_data['mix'] = mix
        return self.cleaned_data

【问题讨论】:

  • 为什么get_or_create(name=artist)clean_artist 中,而不是在通用clean 中;只有在后者中,您才能(像您一样)检查if artist and title
  • 我删除了 clean_artist,现在将其置于通用清理中。无论如何,只有在表单有效的情况下才 get_or_create?

标签: django forms


【解决方案1】:

先检查您的表单在clean() 中是否有效,如何更改比较?

def clean(self):
    ...
    if not artist:
        raise ValidationError("artist is a required field")
    if not title:
        raise ValidationError("title is a required field")
    ...

以上内容为用户提供了一个两步过程,因为如果用户将艺术家和标题都留空,他们只会得到艺术家的通知。 您可以制作更好的(子)if 语句和组合的ValidationError,或者通过使用clean_artistclean_title 来解决这个问题,只是为了提高ValidationError(不在字段清理方法中使用get_or_create):

def clean_artist(self):
    # no get_or_create here
    ...
    if not artist:
        raise ValidationError("artist is a required field")

def clean_title(self):
    # no get_or_create here
    ...  
    if not title:
        raise ValidationError("title is a required field")

def clean(self):
    ...
    if title and artist:
        # get_or_create stuff here
    ...

这样,您应该独立地得到这两个错误,但 get_or_create 仍然在主清理中完成,只有在标题和艺术家有效的情况下。

【讨论】:

    猜你喜欢
    • 2016-02-09
    • 1970-01-01
    • 2012-12-28
    • 1970-01-01
    • 1970-01-01
    • 2022-11-10
    • 2014-10-27
    • 2014-06-19
    • 1970-01-01
    相关资源
    最近更新 更多