2010-10-14 53 views
0

我有一個ManyToMany字段與關係模型。我想要一個formset,按照其中一個 鍵進行篩選,該鍵爲每個其他鍵顯示一個表單。需要Formset與關係模型與一個ForeignKey的所有實例的形式

我的猜測是關係模型上的自定義管理器是解決此問題的關鍵。當數據庫中沒有真實實例時,管理器將返回使用適當的ForeignKey初始化的「幻影」實例。我只是不知道如何讓經理添加「幻影」實例,看起來是爲了過濾現有的實例。

我希望一個例子是值得1K字。

說我希望我的用戶能夠評價相冊。我想用 表示所選樂隊所有專輯的表格。示例模型&視圖

from django.contrib.auth.models import User 
from django.db import models 

class Band(models.Model): 
    name = models.CharField(max_length=30) 

    def __unicode__(self): 
     return self.name 

class Album(models.Model): 
    name = models.CharField(max_length=30) 
    band = models.ForeignKey(Band) 
    ratings = models.ManyToManyField(User, through="Rating") 

    def __unicode__(self): 
     return self.name 


class Rating(models.Model): 
    user = models.ForeignKey(User) 
    album = models.ForeignKey(Album) 
    rating = models.IntegerField() 

    def __unicode__(self): 
     return "%s: %s" % (self.user, self.album) 

# views.py 
from django.forms.models import modelformset_factory 
from django.http import HttpResponseRedirect 
from django.shortcuts import render_to_response 
from django.template.context import RequestContext 
from models import Band, Rating 


RatingFormSet = modelformset_factory(Rating, exclude=('user',), extra=1) 

def update(request): 
    user = request.user 
    band = Band.objects.all()[0] 
    formset = RatingFormSet(request.POST or None, 
         queryset=Rating.objects.filter(album__band=band, 
                user=user)) 

    if formset.is_valid(): 
     objects = formset.save(commit=False) 
     print "saving %d objects" % len(objects) 

     for obj in objects: 
      obj.user = user 
      obj.save()  
     return HttpResponseRedirect("/update/") 

    return render_to_response("rating/update.html", 
           {'formset': formset, 'band':band}, 
           context_instance=RequestContext(request)) 

問題是它只顯示現有關係實例的表單。我如何獲得所有專輯的條目。

謝謝。

回答

0

我再次搜索網絡後又回到了這個問題。我需要定製經理的直覺是錯誤的。我需要的是一個自定義內聯formset,它需要兩個查詢集:一個用於搜索,另一個用要顯示的項目的有序列表。

這種技術的問題是,model_formsets 真的喜歡有現有的實例後跟額外的實例。解決方案是兩個使兩個實例列表顯示:現有的記錄和額外的記錄。然後,在model_formsets創建表單之後,將它們按照顯示順序排序。

要對錶單進行排序,您需要應用我的django補丁[14655]以使表單集合可迭代&然後創建排序迭代器。

所得視圖如下所示:


from django.contrib.auth.models import User 
from django.forms.models import inlineformset_factory, BaseInlineFormSet, \ 
    BaseModelFormSet, _get_foreign_key 
from django.http import HttpResponseRedirect 
from django.shortcuts import render_to_response 
from django.template.context import RequestContext 
from models import Band, Rating 

class list_qs(list): 
    """a list pretending to be a queryset""" 
    def __init__(self, queryset): 
     self.qs = queryset 
    def __getattr__(self, attr): 
     return getattr(self.qs, attr) 

class BaseSparseInlineFormSet(BaseInlineFormSet): 
    def __init__(self, *args, **kwargs): 
     self.display_set = kwargs.pop('display_set') 
     self.instance_class = kwargs.pop('instance_class', self.model) 
     # extra is limited by max_num in baseformset 
     self.max_num = self.extra = len(self.display_set) 
     super(BaseSparseInlineFormSet, self).__init__(*args, **kwargs) 

    def __iter__(self): 
     if not hasattr(self, '_display_order'): 
      order = [(i, obj._display_order) 
         for i, obj in enumerate(self._instances)] 
      order.sort(cmp=lambda x,y: x[1]-y[1]) 
      self._display_order = [i[0] for i in order] 
     for i in self._display_order: 
      yield self.forms[i] 

    def get_queryset(self): 
     if not hasattr(self, '_queryset'): 
      # generate a list of instances to display & note order 
      existing = list_qs(self.queryset) 
      extra = [] 
      dk = _get_foreign_key(self.display_set.model, self.model) 
      for i, item in enumerate(self.display_set): 
       params = {dk.name: item, self.fk.name: self.instance} 
       try: 
        obj = self.queryset.get(**params) 
        existing.append(obj) 
       except self.model.DoesNotExist: 
        obj = self.instance_class(**params) 
        extra.append(obj) 
       obj._display_order = i 
      self._instances = existing + extra 
      self._queryset = existing 
     return self._queryset 

    def _construct_form(self, i, **kwargs): 
     # make sure "extra" forms have an instance 
     if not hasattr(self, '_instances'): 
      self.get_queryset() 
     kwargs['instance'] = self._instances[i] 
     return super(BaseSparseInlineFormSet, self)._construct_form(i, **kwargs) 


RatingFormSet = inlineformset_factory(User, Rating, formset=BaseSparseInlineFormSet) 

def update(request): 
    band = Band.objects.all()[0] 
    formset = RatingFormSet(request.POST or None, 
          display_set=band.album_set.all(), 
          instance=request.user) 
    if formset.is_valid(): 
     objects = formset.save(commit=False) 
     print "saving %d objects" % len(objects) 

     for obj in objects: 
      obj.save() 

     return HttpResponseRedirect("/update/") 

    return render_to_response("rating/update.html", 
           {'formset': formset, 'band':band}, 
           context_instance=RequestContext(request)) 
相關問題