2012-03-10 10 views
2

の保存方法で私のブログの記事のモデルはタグの多対多のフィールドがあります。Djangoのadmin.py:save_model()(model.saveによって呼び出されていない)のModelForm

tags = models.ManyToManyField(PostTag) 

をしかし、それはでしたその後、私は私のadmin.pyを修正

def _get_tagging(self): # Returns comma separated list of tags 
    tagging = [] 
    for tag in self.tags.all(): 
     tagging.append(tag.name) 
    return ", ".join(tagging) 

def _set_tagging (self, tagging): # Saves tags from comma separated list 
    tagging = tagging.split(", ") 
    self.tags.clear() 
    for tag in tagging: 
     if len(tag) < 1: 
      continue 
     try: 
      self.tags.add(PostTag.objects.get(name=tag)) 
     except ObjectDoesNotExist: 
      self.tags.create(name=tag) 

tagging = property(_get_tagging, _set_tagging) 

:それを編集して不快と私はこのような私のモデルを修正

class BlogAdminForm (forms.ModelForm): 
    tagging = forms.CharField(required=False, label="Tags", max_length=200, 
         widget=forms.TextInput(attrs={'class':'vTextField'})) 

    class Meta: 
     model = BlogPost 

    def __init__(self, *args, **kwargs): 
     super(BlogAdminForm, self).__init__(*args, **kwargs) 

     if kwargs.has_key('instance'): 
      instance = kwargs['instance'] 
      self.initial['tagging'] = instance.tagging 

    def save(self, commit=True): 
     model = super(BlogAdminForm, self).save(commit=False) 
     model.tagging = self.cleaned_data["tagging"] 

     if commit: 
      model.save() 

     return model 

そして、これはうまくいきましたが、オブジェクトを編集する場合にのみ有効です。新しいオブジェクトを作成しようとしたときにエラーが発生しました。どうして?多対多リレーションシップは、データベースにまだ存在せず、プライマリキーを持たないオブジェクト(「BlogPost」インスタンスはプライマリキー値を持つ必要があります。利用される)。私はこのようにsaveメソッドを編集して解決しようとしました:

def save(self, commit=True): 
    model = super(BlogAdminForm, self).save(commit=False) 
    try: 
     model.tagging = self.cleaned_data["tagging"] 
    except ValueError: 
     model.save() 
     model.tagging = self.cleaned_data["tagging"] 

    if commit: 
     model.save() 

これは元の問題を解決しました。しかし、今model.save()は私の管理モデルのsave_modelメソッドを呼び出していません。この結果で

class BlogAdmin (admin.ModelAdmin): 
    # ... 
    form = BlogAdminForm 

    def save_model(self, request, obj, form, change): 
     obj.author = request.user 
     obj.save() 

を私は新しいエラーを取得する:null value in column "author_id" violates not-null constraint.私は間違って何をしているのですか?このメソッドを手動で呼び出すことはできますか?

答えて

3

インスタンスを保存した後にタグを保存する必要があります。これは、save_model関数で行うことを意味します。これは、タグ・操作コードとは何の関係もありません:あなたはdocumentation for the Form.save methodを見れば、それは言う:あなたの問題を解決するためにいくつかの方法があります

Another side effect of using commit=False is seen when your model has a many-to-many relation with another model. If your model has a many-to-many relation and you specify commit=False when you save a form, Django cannot immediately save the form data for the many-to-many relation. This is because it isn't possible to save many-to-many data for an instance until the instance exists in the database.

To work around this problem, every time you save a form using commit=False , Django adds a save_m2m() method to your ModelForm subclass. After you've manually saved the instance produced by the form, you can invoke save_m2m() to save the many-to-many form data.

。タグIDとカンマ区切りのタグ名のリストの間を行き来するwidgetと書いてsave_modelメソッドでform.save_m2m()を呼び出すことができます。しかし、この方法は、フォームが保存されていなくても(おそらく、フォーム内の別の場所での検証エラーのために)ウィジェットから値をデコードするときに新しいタグを作成する必要があるという欠点があります。私は、フォームにタグ操作コードを移動

class BlogAdminForm(forms.ModelForm): 
    tagging = forms.CharField(required=False, label="Tags", max_length=200, 
           widget=forms.TextInput(attrs={'class':'vTextField'})) 

    class Meta: 
     model = Post 

    def __init__(self, *args, **kwargs): 
     super(BlogAdminForm, self).__init__(*args, **kwargs) 
     if 'instance' in kwargs: 
      tags = (t.name for t in kwargs['instance'].tags.all()) 
      self.initial['tagging'] = ', '.join(tags) 

    def save_tags(self, obj): 
     obj.tags = (Tag.objects.get_or_create(name = tag.strip())[0] 
        for tag in self.cleaned_data['tagging'].split(',')) 

class BlogPostAdmin(admin.ModelAdmin): 
    form = BlogAdminForm 

    def save_model(self, request, obj, form, change): 
     obj.author = request.user 
     obj.save() 
     form.save_tags(obj) 

注:

だから、私はこのケースでより良いアプローチは、フォームに独自のsave_tagsメソッドを追加することだと思う私は、それはここに属していると思いますユーザー入力に関するものなので、モデルではありません。

  • 'instance' in kwargskwargs.has_key('instance')よりも簡単です:私はまた、文体の改善のカップルを作りました。

  • ジェネレータの表現(t.name for t in kwargs['instance'].tags.all())は、ループをforループで作成するよりも簡単です。

  • get_or_create methodはタグが変更されないときは、代わりにclear、その後addを呼び出すのでManyToManyフィールドに直接割り当てることができますtry: ... except ObjectDoesNotExist: ...

  • への必要性を避けるための便利なショートカットが(また、それは、より効率的ですです)。

+0

は、このような偉大な答えをありがとうございました!また、文体の改善に感謝します。私は自分の仕事でそれらを考慮に入れます。 –

関連する問題