在 Django 中,如果我们有如下模型:
class SettingField(models.Model):
name = models.CharField(max_length=200)
value = models.TextField(max_length=200)
class Setting(models.Model):
setting_type = models.CharField(max_length=1, choices=STYPES)
fields = models.ManyToManyField(SettingField, null=True)
并且我们有一个表单:
class SettingsForm(forms.Form):
login = forms.CharField(
required=True,
label=u'Login'
)
password = forms.CharField(
widget=forms.PasswordInput,
required=True,
label=u'Password'
)
在视图中,我们可以保存设置和自定义字段:
if request.method == 'POST':
form = SettingsForm(request.POST)
if form.is_valid():
setting = Setting()
setting.setting_type = 'd'
setting.save()
for ff in form.fields:
setting_field = SettingField()
setting_field.name = ff
setting_field.value = form.cleaned_data[ff]
setting_field.save()
setting.fields.add(setting_field)
现在,我们要获取这些数据:
s_objs = Setting.objects.filter(setting_type='d')
logins = list()
for so in s_objs:
logins.append({
"login": so.fields.??? what here ???,
"password": so.fields.??? what here ???
})
问题是我们需要获取 so.fields
的值,但 so.fields
是一个 ManyToManyField,我们无法直接访问其中的值。
2、解决方案
一种方法是使用循环和比较名称来获取值:
for so in s_objs:
for field in so.fields.all():
if field.name == 'login':
login = field.value
elif field.name == 'password':
password = field.value
但是,这会增加查询次数并降低效率。一种更好的方法是使用 预取(prefetch_related) 来获取所有相关字段:
s_objs = Setting.objects.filter(setting_type='d').prefetch_related('fields')
for so in s_objs:
logins.append({
"login": so.fields.all()[0].value,
"password": so.fields.all()[1].value
})
这样,我们只需要一次查询就可以获取所有相关字段。
代码例子
from django.db import models
from django.forms import ModelForm, CharField, forms
from django.db.models import prefetch_related_objects
# Models
class SettingField(models.Model):
name = models.CharField(max_length=200)
value = models.TextField(max_length=200)
class Setting(models.Model):
setting_type = models.CharField(max_length=1, choices=[('d', 'Default')])
fields = models.ManyToManyField(SettingField, null=True)
# Forms
class SettingsForm(ModelForm):
class Meta:
model = Setting
fields = ['setting_type', 'fields']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['fields'].queryset = SettingField.objects.all()
# Views
def settings_view(request):
if request.method == 'POST':
form = SettingsForm(request.POST)
if form.is_valid():
setting = form.save()
for field in form.cleaned_data['fields']:
setting.fields.add(field)
return HttpResponseRedirect(reverse('settings'))
else:
form = SettingsForm()
settings = Setting.objects.filter(setting_type='d').prefetch_related('fields')
logins = []
for setting in settings:
logins.append({
"login": setting.fields.all()[0].value,
"password": setting.fields.all()[1].value
})
context = {
'form': form,
'logins': logins
}
return render(request, 'settings.html', context)
在模板中,我们可以使用如下代码来显示获取到的值:
{% for login in logins %}
<p>Login: {
{ login.login }}</p>
<p>Password: {
{ login.password }}</p>
{% endfor %}