2010-10-14 51 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)) 
相关问题