如何从 ManyToMany 字段获取值?

在 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 %}

猜你喜欢

转载自blog.csdn.net/D0126_/article/details/143404355