Django, возможно ли создать множественный выбор статуса с моделью? - PullRequest
0 голосов
/ 23 апреля 2020

Это фактически мой models.py, который дает мне возможность создать статус для множественного выбора:

class Status(models.Model):
      slug = models.SlugField(unique=True)
      category = models.TextField()


class Example(models.Model):
      category= models.ForeignKey(Status)

Я хочу иметь возможность добавить подкатегорию. Другими словами, если я создаю новую категорию «Продукт» в моем status model, я хочу иметь возможность создать выбор подкатегории, dipendet из категории и выбрать в моей модели примера в новом поле с именем sub_category.

Пример объявления: с помощью Status.models клиент может создать категорию продукта, например, «автомобиль» и связанную с ней подкатегорию «колеса», «руль» и так далее. После, когда заполните Example.models и выберите в категории «Автомобиль», клиент может выбрать только подкатегорию, сохраненную ранее («Whell», «Рулевое колесо»). Я надеюсь, что теперь мое объяснение лучше

1 Ответ

1 голос
/ 23 апреля 2020

Вы можете установить внешний ключ для одной и той же модели несколько раз, но вам необходимо включить аргумент related_name.

Все Python way

# models.py

class Status(models.Model):
    slug = models.SlugField(unique=True)
    category = models.TextField()
    # This allows us to have heirarchy of categories.
    parent =  models.ForeignKey(
        "self", 
        null=True,
        blank=True,
        related_name="children", 
        related_query_name="child",
        on_delete=models.PROTECT,
    )


class Example(models.Model):
    category = models.ForeignKey(
        Status, 
        related_name="example_categories",
        limit_choices_to={'parent': None},
    )
    # This field is hidden on new objects.
    sub_category = models.ForeignKey(
        Status,
        related_name="example_sub_categories",
        null=True,
        blank=True,
    )
# forms.py

class ExampleForm(forms.ModelForm):
    class Meta:
        model = Example
        fields = '__all__'

    def __init__(self, *args, **kwargs):
        # When we instantiate the form, we check if the object 'instance' exists.
        # If it does not, we hide the `sub_category` field.
        # Otherwise we restrict the queryset to children of the `category` field.

        # NOTE: this will need to be properly cleaned. It does not prevent editing
        # the parent `category` after the `sub_category` has already been saved.
        instance = kwargs.get('instance')
        if not instance:
            self.fields.get('sub_category').widget = forms.HiddenInput()
        else:
            sub_cateogries = Status.objects.filter(parent=instance.category).all()
            self.fields.get('sub_category').queryset = sub_cateogries

# admin.py

@admin.register(Example)
class ExampleAdmin(admin.ModelAdmin):
    form = ExampleForm

Этот метод страдает от необходимости сохранять объект один раз, а затем иметь возможность редактировать подкатегории. Это не идеально, поскольку добавляет второй шаг к циклу управления объектами.

Лучший путь

Лучший способ справиться с этим более сложный и требует некоторого JS, поэтому Я не буду предоставлять код здесь. Однако я объясню, что это такое и как это можно сделать.

Вы можете использовать Select2 для выполнения AJAX запроса к пользовательскому представлению администратора, которое возвращает список подкатегорий в виде JSON массив.

Добавление пользовательского представления администратора достаточно просто, просто проверьте user.is_staff и соответствующие разрешения. Вы можете добавить URL, переопределив метод get_urls() в вашем классе ExampleAdmin. Нечто подобное работает ...

def get_urls(self):
    return [
        path(
            "auto_field/", 
            self.admin_site.admin_view(YourView.as_view()), 
            name='example_select2',),
    ] + super().get_urls()

Возможность вносить изменения такого рода является основной для возможности поднять ваш Django на следующий уровень. Первый вариант работает, но go со вторым, если можете.

...