Предварительное заполнение формы модели на основе url

Я испытываю трудности с формой Django. Мне не очень удобно работать с этой частью фреймворка. Дело обстоит следующим образом. У меня есть форма ModelForm, содержащая поле OneToOneField. Я хотел бы предварительно заполнить поле ModelChoice объектом, на который ссылается URL. Например: catalog/location/add/7, чтобы добавить местоположение к продукту с pk=7. NB: если PK не включен, форма просто не будет заполнена

models.py

class Product(models.Model):
   pass

class ProductLocation(geo_models.Model):
   product = geo_models.OneToOneField(Product, verbose_name=_('Product'), on_delete=geo_models.CASCADE, null=True,)
   geo_point = geo_models.PointField(verbose_name=_('Location'), blank=True)
   address = geo_models.CharField(verbose_name=_('Address'), max_length=255)

urls.py

urls += [
            re_path(r"^location/add/(?P<product_pk>\d+)/$",
                 self.add_update_location.as_view(),
                 name='catalogue-product-location-add'),  # Prepopulate product ModelChoiceField

            path("location/add",
                 self.add_update_location.as_view(),
                 name='catalogue-location-create'  # Empty product ModelChoiceField
                 ),

            path("location/<int:pk>",
                 self.add_update_location.as_view(),
                 name='catalogue-location-update')  # Edit an existing Location
        ]

views.py

class LocationCreateUpdateView(generic.UpdateView):
    form_class = ProductLocationForm

    creating = True
    product = None

    def get_object(self, queryset=None):
        """
        Extend the role of get_object to retrieve the product specified in the url
        """
        self.creating = "pk" not in self.kwargs
        if self.creating:
            if self.kwargs.get("product_pk"):
                self.product = get_object_or_404(Product, pk=self.kwargs["product_pk"])
            return None
        else:
            return super().get_object(queryset)

# Try this way
    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        if self.product and not self.request.POST.get('product'):
            kwargs["product"] = self.product
        return kwargs

# Or this way
    def get_initial(self):
        initial = super().get_initial()
        if self.product:
            initial["product"] = self.product
        return initial

forms.py


class ProductLocationForm(forms.ModelForm):
    def __init__(self, product=None, instance=None, *args, **kwargs):
        # I tried this
        if not instance and product:
            instance = ProductLocation(product=product)
        super().__init__(instance=instance, *args, **kwargs)

Я попытался переопределить get_initial() для передачи товара в форму, но форма не прошла валидацию, просит выбрать товар из списка и, более того, стирает исходное значение.

Я также использую get_form_kwargs() для передачи продукта в функцию form init и пытаюсь :

  • прикрепите товар к экземпляру ProductLocation
  • .
  • установить начальные данные в init
  • установить self.fidels['product'].initial = product

Спасибо за помощь

PS : несколько ссылок на SO

Основная причина, по которой это не сработает, заключается в том, что UpdateView действительно вызовет .get_object(..), который установит self.instance в результат .get_object(…). Это не всегда нужно в зависимости от вашего случая использования. Мы можем сделать это необязательным, возвращая None в get_object(), если pk не является частью self.kwargs:

from django.shortcuts import get_object_or_404


class LocationCreateUpdateView(generic.UpdateView):
    model = ProductLocation
    form_class = ProductLocationForm

    def get_object(self, queryset=None):
        creating = self.creating = 'pk' not in self.kwargs
        if not creating:
            return super().get_object(queryset)

    def get_initial(self):
        initial = super().get_initial()
        if 'product_pk' in self.kwargs:
            initial['product'] = get_object_or_404(
                Product, pk=self.kwargs['product_pk']
            )
        return initial
Вернуться на верх