Создание модели ManyToMany с обязательным полем

Есть модельки Городов и Туров. Тур может проходить по нескольким городам, в городе может проходить несколько туров. Логично - Many To Many. Но городу никто не запрещает существовать без тура, а вот тур 100% должен проходить как миниму по одному городу. Т.е. при создании города я могу не указывать туры, а вот при создании тура я обязан указать город.

class Tour(UUIDMixin, NameMixin, Model):
    description = TextField(_('description'))
    cities = ManyToManyField(
        'City',
        verbose_name = _('cities'),
        through = 'TourCity'
    )

    def __str__(self) -> None:
        return self.name

    class Meta:
        db_table = '"tours_data"."tour"'
        ordering = ['name']
        verbose_name = _('tour')
        verbose_name_plural = _('tours')
        unique_together = (('name', 'description', 'cities'),)

class City(UUIDMixin, NameMixin, Model):
    country = CharField(
        _('country'),
        null=False,
        blank=False,
        max_length=COUNTRY_MAX_LEN
    )

    def __str__(self) -> None:
        return f'{self.name}, {self.country}'

    class Meta:
        db_table = '"tours_data"."city"'
        ordering = ['name']
        verbose_name = _('city')
        verbose_name_plural = _('cities')
        unique_together = (('name', 'country'),)

class TourCity(UUIDMixin, Model):
    tour = ForeignKey(Tour, verbose_name=_('tour'), on_delete=CASCADE)
    city = ForeignKey(City, verbose_name=_('city'), on_delete=CASCADE, null=False, blank=False)

    class Meta:
        db_table = '"tours_data"."tour_city"'
        unique_together = (('tour', 'city'),)
        verbose_name = _('relationship tour city')
        verbose_name_plural = _('relationships tour city')

Однако я все еще могу создать тур и не добавить в него ни одного города. Как это можно исправить?

Для проверки помогают инлайны.

Поле min_num просто не даст создать запись в таблице если не удет заполнен хотя бы один инлайн. Он проверяет не то что инлайн просто добавлен, но и что в нем хранится значение.

Есть еще max_num - но она не для моего случая, может кому понадобится.

Итак, в admin.py делаем инлайн:

class TourCityInline(admin.TabularInline):
    model = TourCity
    min_num = 1

Который в последствии применяем ко всем нужным классам в админ-панели:

@admin.register(Tour)
class TourAdmin(admin.ModelAdmin):
    model = Tour
    inlines = (TourCityInline,)
Вернуться на верх