Django get_or_create仅在满足表单约束时

时间:2016-10-08 19:42:30

标签: django forms

我有一个表格,要求一首歌的艺术家,标题和混音。艺术家和标题是必填字段,但Mix不是。如果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

1 个答案:

答案 0 :(得分: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")
    ...

以上为用户提供了两个步骤,因为如果用户将艺术家和标题都留空,他们就会得到艺术家的注意。 你可以创建一个更好的(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仍然在主干净中完成。