Django:在一个视图中多次出现相同的形式

时间:2014-02-18 12:37:00

标签: django django-1.6

我有三个模型User(django.contrib.auth),ScreeningUser_ScreeningUser_Screening是一个m2m表,其中包含额外字段status

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

class Screening(models.Model):
    title = models.CharField(max_length=255)
    start = models.DateTimeField()
    user_relation = models.ManyToManyField(User, blank=True,
        through='User_Status')

class User_Status(models.Model):
    ATTENDING = 'c'
    NOT_ATTENDING = 'n'
    PROJECTION = 'p'
    STATUS_CHOICES = (
        (ATTENDING, 'attending'),
        (NOT_ATTENDING, 'not attending'),
        (PROJECTING, 'projecting'),
    )
    screening = models.ForeignKey(Screening)
    user = models.ForeignKey(User)
    status = models.CharField(max_length=1, choices=STATUS_CHOICES)

现在我想制作一个视图,显示所有即将进行的放映。到目前为止,这很容易:

#views.py
@login_required()
def index(request):
    current_screenings = Screening.objects.filter(start__gte=timezone.now())
    context = {'current_screenings': current_screenings}
    return render(request, 'schedule/index.html', context)

在此视图中,登录用户应该能够更新status(来自User_Screening表)。也可能是,用户还没有此筛选的记录,因此应该创建一个。

我不明白,如何为每次筛选存档表单下拉字段,用户可以在其中选择其状态。 (?如果尚未设置状态,attendingnot attendingprojection

根据我的理解,我需要多种形式,知道与他们有关的筛选。

此外,Formsets似乎不起作用,因为我无法始终用初始数据填写表单,因为某些或所有放映可能会丢失记录。此外,我不知道哪种形式属于哪种筛选对象。

更新 我想在HTML中最终得到的是这样的:

<form>
  <h1>Current Screening 1</h1>
    <select onchange="submit()" name="screening_user" id="s1">
      <option value="att">Attending</option>
      <option value="not_att">Not Attending</option>
      <option selected="selected" value="pro">Projection</option>
    </select>
  <h1>Current Screening 2</h1>
    <select onchange="submit()" name="screening_user" id="s2">
      <!-- The 'Please Select' option is only visible, if the user does not
        have a relation in 'User_Screening' for this screening -->
      <option selected="selected" value="none">Please Select</option>
      <option value="att">Attending</option>
      <option value="not_att">Not Attending</option>
      <option value="pro">Projection</option>
    </select>
  <!-- More Screenings -->
  <h1>Current Screening n</h1>
    <!-- select for screening n -->
</form>

因此,根据登录用户,需要从预加载数据的相同表单中更改表单数量。

3 个答案:

答案 0 :(得分:0)

如果筛选与用户的m2m关系,则参加用户可以在该列表中。如果不参加......那么,他们没有参加!这有意义吗?

class Screening(models.Model):
    title = models.CharField(max_length=255)
    date = models.DateTimeField()
    attending = models.ManyToManyField(User)

形式:

class ScreeningForm(ModelForm):
    class Meta:
        model = Screening
        fieds = ['attending', ]

表单集:

ScreeningFormSet = modelformset_factory(Screenig, max_num=1)
formset = ScreeningFormSet(Screening=Screening.objects.filter(date__gte=now))

答案 1 :(得分:0)

一方面,您可以通过ajax请求发送表单数据。在该请求中,您只需发送一个表单并处理数据。您不需要任何表单集。根据您的用例,这可能会给您的服务器增加不必要的流量。

另一种解决方案是在数据库中没有筛选用户组合条目的情况下,将另一个STATUS_CHOICE添加为“未选择”作为默认值。在视图的POST处理程序中,您可以只检查表单数据是否设置为此值。在这种情况下,您只需忽略表单。如果是另一个值,则相应地设置db条目。

答案 2 :(得分:0)

在fenode的#django的帮助下,我解决了我的问题。最后,我坚持使用formsets。

考虑到我的问题中的models.py,我必须稍微更改User_Status,如果筛选没有关系,则为Select-Widget添加NO_STATUS选项。请注意,NO_STATUS不是model.CharField的选择!

#models.py
class User_Status(models.Model):
NO_STATUS = '?'
PROJECTIONIST = 'p'
ATTENDING = 'c'
NOT_ATTENDING = 'n'
STATUS_CHOICES = [
    (ATTENDING, 'Anwesend'),
    (NOT_ATTENDING, 'Nicht anwesend'),
    (PROJECTIONIST, 'Vorführer'),
]
STATUS_CHOICES_AND_EMPTY = [(NO_STATUS, 'Please choose')] + STATUS_CHOICES
screening = models.ForeignKey(Screening)
user = models.ForeignKey(User)
status = models.CharField(max_length=1, choices=STATUS_CHOICES,
    default=ATTENDING)

接下来,表格。修改后的__init__注意,“请选择”只是一个有效的选择,如果将其设置为status的初始值。否则,选择不会显示。

#forms.py
class ScreeningUserStatusForm(forms.Form):
    screening_id = forms.IntegerField(min_value=1)
    status = forms.ChoiceField(choices=User_Status.STATUS_CHOICES_AND_EMPTY, 
        widget=forms.Select(attrs={"onChange":'submit()'}))

    def __init__(self, *args, **kwargs):
        super(ScreeningUserStatusForm, self).__init__(*args, **kwargs)
        if self['status'].value() != User_Status.NO_STATUS:
            #Once, a status is selected, the status should not be unset.
            self.fields['status'].choices=User_Status.STATUS_CHOICES

最后是视图,它使用formset将所有当前屏幕放入其中。

def update_user_status(screening, user, status):
    #Get old status, if already exists.
    new_status = User_Status.objects.get_or_create(screening=screening,
        user=user)

    # Add to selected status
    new_status.status = status 
    new_status.save()

@login_required()
def index(request):
    """
    displays all upcoming screenings
    """

    # Get current screenings
    current_screening_set = Screening.objects.filter(start__gte=timezone.now() - datetime.timedelta(hours=24)).order_by('start')
    current_screening_list = current_screening_set.values('id')

    ScreeningFormSet = formset_factory(ScreeningUserStatusForm, extra=0)

    if request.method == 'POST':
        #Get a formset bound to data from POST
        formset = ScreeningFormSet(request.POST, request.FILES)
        if formset.is_valid():
            for form in formset.cleaned_data:
                s = get_object_or_404(Screening, pk=form['screening_id'])
                if form['status'] != User_Status.NO_STATUS:
                    update_user_status(screening=s, user=request.user, status=form['status'])
    else:
        #create a fresh formset
        for form_data in current_screening_list:
            screening = Screening.objects.get(pk=form_data['id'])
            status = User_Status.objects.filter(user=request.user, screening=screening)
            if status.count() != 1:
                form_data['status'] = u'?'
            else:
                form_data['status'] = status.first().status
            form_data['screening_id'] = form_data['id']

        formset = ScreeningFormSet(initial=current_screening_list)

    forms_and_curr_screenings = zip(formset.forms, current_screening_set)

    context = {'formset' : formset, 'current_screenings' : forms_and_curr_screenings}
    return render(request, 'schedule/index.html', context)

formset.formscurrent_screening_set一起压缩,以便为每个人提供额外的数据。另外,formset的模板也会management_form

模板看起来像这样

<!-- index.html -->
{% if current_screenings %}
    <form method="post">
    {{ formset.management_form }}
    {% csrf_token %}
    <table>
      <thead>
        <tr>
          <th>Screening</th>
          <th>My Status</th>
        </tr>
      </thead>
      <tbody>
      {% for form, screening in current_screenings %}
        <tr>
          <td>{{ screening }}</a></td>
          <td>
            {{ form.screening_id.as_hidden }}
            {{ form.status }}
          </td>
        </tr>
      {% endfor %}
      </tbody>
    </table>
  </form>
{% endif %}