从管理器返回自定义模型实例

0 投票
1 回答
531 浏览
提问于 2025-04-17 01:53

我有一个模型,它的结构是这样的,存储数据的方式是键值对。

class Setting(models.Model):
    company = models.ForeignKey(
        Company
    )
    name = models.CharField(
        null=False, max_length=255
    )
    value= models.CharField(
        null=False, max_length=255
    )

在这个模型上,我有一个自定义的管理器,它重写了 get 方法。当我像这样查询我的模型 Settings.objects.get(company=1) 时,我使用我重写的 get 方法来执行 self.objects.filter(company=1),这样会返回一个对象列表。我想知道是否可以生成一个包含所有键值对作为字段的自定义查询集。

举个例子:

如果我模型中的数据是这样的:

company  name    value
-------  ----    -----
1        theme   custom
1        mode    fast
1        color   green

当有人执行 Settings.objects.get(company=1) 时,我希望返回一个像这样转换过的查询集:

company  theme    mode     color
------   -----    ----     -----
1        custom   fast     green

我尽量详细说明了,但如果需要我解释得更清楚,请告诉我。我不确定Django模型是否支持这种情况。

谢谢大家。


编辑:使用代理模型

我能否通过使用代理模型来实现这个目标?也就是说,有一个基础模型来存储键值字段,然后用一个自定义的代理模型来使用正常的 getsave 方法?

1 个回答

1

这是我怎么做的。

我之所以这么做,是因为我有一个模型,它以键值对的形式存储信息。我需要在这个模型上构建一个ModelForm,但这个ModelForm应该把键值对显示为字段,也就是把行转换成列。默认情况下,模型的get()方法总是返回它自己的一个实例,而我需要使用一个自定义的模型。我的键值对模型长这样:

class Setting(models.Model):
    domain = models.ForeignKey(Domain)
    name = models.CharField(null=False, max_length=255)
    value = models.CharField(null=False, max_length=255)

    objects = SettingManager()

我在这个模型上建立了一个自定义管理器,以覆盖get()方法:

class SettingManager(models.Manager):

    def get(self, *args, **kwargs):
        from modules.customer.proxies import *
        from modules.customer.models import *

        object = type('DomainSettings', (SettingProxy,), {'__module__' : 'modules.customer'})()
        for pair in self.filter(*args, **kwargs): setattr(object, pair.name, pair.value)

        setattr(object, 'domain', Domain.objects.get(id=int(kwargs['domain__exact'])))
        return object

这个管理器会实例化一个这个抽象模型的实例。(抽象模型没有表,所以Django不会报错)

class SettingProxy(models.Model):

    domain = models.ForeignKey(Domain, null=False, verbose_name="Domain")
    theme = models.CharField(null=False, default='mytheme', max_length=16)
    message = models.CharField(null=False, default='Waddup', max_length=64)

    class Meta:
        abstract = True

    def __init__(self, *args, **kwargs):
        super(SettingProxy, self).__init__(*args, **kwargs)
        for field in self._meta.fields:
            if isinstance(field, models.AutoField):
                del field

    def save(self, *args, **kwargs):
        with transaction.commit_on_success():
            Setting.objects.filter(domain=self.domain).delete()

            for field in self._meta.fields:
                if isinstance(field, models.ForeignKey) or isinstance(field, models.AutoField):
                    continue
                else:
                    print field.name + ': ' + field.value_to_string(self)
                    Setting.objects.create(domain=self.domain,
                        name=field.name, value=field.value_to_string(self)
                    )

这个代理模型包含了我想在ModelForm中显示的所有字段,并且在我的模型中以键值对的形式存储。现在,如果我需要添加更多字段,我只需修改这个抽象模型,而不必编辑实际的模型。现在我有了一个模型,我可以像这样简单地在它上面构建一个ModelForm:

class SettingsForm(forms.ModelForm):

    class Meta:
        model = SettingProxy
        exclude = ('domain',)

    def save(self, domain, *args, **kwargs):
        print self.cleaned_data
        commit = kwargs.get('commit', True)
        kwargs['commit'] = False
        setting = super(SettingsForm, self).save(*args, **kwargs)
        setting.domain = domain
        if commit:
            setting.save()
        return setting

希望这对你有帮助。为了弄清楚这一点,我查阅了很多API文档。

撰写回答