我有三个模型User
(django.contrib.auth),Screening
和User_Screening
。 User_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
表)。也可能是,用户还没有此筛选的记录,因此应该创建一个。
我不明白,如何为每次筛选存档表单下拉字段,用户可以在其中选择其状态。 (?
如果尚未设置状态,attending
,not attending
或projection
)
根据我的理解,我需要多种形式,知道与他们有关的筛选。
此外,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>
因此,根据登录用户,需要从预加载数据的相同表单中更改表单数量。
答案 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.forms
与current_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 %}