Вот как я это сделал.
Мне нужно было сделать это, потому что у меня была Модель, которая хранила информацию в виде пар ключ-значение, и мне нужно было построить 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)
)
Этот прокси-сервер имеет все поля, которые я хотел бы отобразить в моем ModelFom и сохранить как пары ключ-значение в моей модели. Теперь, если мне когда-нибудь понадобится добавить больше полей, я могу просто изменить эту абстрактную модель и не нужно редактировать саму модель. Теперь, когда у меня есть модель, я могу просто построить на ней 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.