Получить все данные записей в виджет без создания нового запроса в базе данных

У меня есть 2 модели продукта и категории с - ManyToMany Relation Product-Category и self ForeignKey для категории

 class Category(models.Model):
    parent = models.ForeignKey('self', blank=True, null=True, verbose_name='parent category', on_delete=models.CASCADE)

class Product(models.Model):
    categories = models.ManyToManyField(Category)
    name = models.CharField(max_length=255)

и форму создания продукта:

class ProductModelForm(ModelForm):

        class Meta:
            model = Product
            fields = ['categories', 'name', 'short_description', 'description']
            widgets = {
                'categories': MyWidget,
            }

По умолчанию ManyToMany становится ModelMultipleChoiceField и соответствует SelectMultiple Widget.

Я создаю новый виджет, наследуемый от SelectMultiple.

Моя проблема в том, что во всех виджетах по умолчанию Django отправляет из записи только значение, определенное внутри def __str__ в модели. В случае Select также pk.

(None, [{'name': 'categories', 'value': 7, 'label': 'Category 2', 'selected': True, 'index': '1', 'attrs': {'selected': True}, 'type': 'select', 'template_name': 'django/forms/widgets/select_option.html'}]

Мне нужно иметь в виджете и другую информацию, такую ​​как родительское значение.

Конечно, я могу сделать еще один запрос и получить данные из базы данных, но это означает, что у меня есть 2 запроса, один я и один Джанго.

Есть ли способ заставить Django отправлять все данные записи в виджет, а не только то, что было определено в __str__?

Изменение __str__ не вариант, потому что он используется и в других местах и ​​в админке.


person user3541631    schedule 22.11.2017    source источник


Ответы (1)


Это не очень просто: ModelChoiceFieldModelMultipleChoiceField) принимает параметр queryset, который по умолчанию является default_manager (Category.objects.all()) модели. Набор запросов используется, когда для виджета создаются choices с использованием ModelChoiceIterator. Это действительно возвращает только 2-кортеж объекта id и label для каждого объекта в наборе запросов.

Таким образом, чтобы убедиться, что есть только один запрос к базе данных, вам нужно создать подкласс ModelMultipleChoiceField, чтобы использовать подкласс ModelChoiceIterator, который возвращает 3-кортеж (или более) с нужными вам параметрами. Затем убедитесь, что вы также подклассифицируете метод __init__() метода ChoiceWidget (в вашем случае SelectMultiple), чтобы снова разделить кортеж на 2-кортеж, чтобы назначить self.choices, а другие значения установить как атрибуты виджета (self.attrs), чтобы вы могли использовать их в своем шаблон.

Не уверен, что это стоит той оптимизации, которую вы получаете, особенно если вы используете хороший механизм кэширования, вы на самом деле не попадете в свою базу данных дважды, если выполните Category.objects.all() два раза.

person dirkgroten    schedule 22.11.2017
comment
Я думал о подклассе ModelMultipleChoiceField, но вызывается ManytoMany (используя метод fields_for_model), где я не вижу, как я могу перезаписать, чтобы изменить поле по умолчанию, не затрагивая ядро ​​​​или другие части. - person user3541631; 22.11.2017
comment
ну, вы можете просто переопределить поле формы для использования в форме: просто добавьте categories = MyOwnModelMultipleChoiceField(queryset=Category.objects.all()) вверху формы. - person dirkgroten; 22.11.2017
comment
но опять же, если честно, это хорошее упражнение для понимания внутренней работы Django, но я бы не пошел по этому пути :-) Добавьте хорошее кэширование, и вам не придется беспокоиться об этом. - person dirkgroten; 22.11.2017