Django get_or_create只在满足表单约束时使用



我有一个表单,要求提供歌曲的Artist, Title和Mix。艺术家和标题是必需的字段,但混合不是。只有当艺术家、标题和混合不存在时,表单才应该保存。如果表单有空白的艺术家或标题字段,它应该在提交时显示"此字段是必需的"。我遇到的问题是,如果标题字段是空的,但艺术家被填充,它仍然会用get_or_create创建艺术家对象(见下面的###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

如何改变你的比较,首先检查如果你的形式是有效的clean() ?

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

对用户来说是一个两步的过程,因为如果用户让artist和title都为空,他们只会得到artist通知。您可以创建一个更好的(sub) 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仍然在主清理中完成,只有当标题和艺术家是有效的。

最新更新