Предварительное заполнение формы модели на основе 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
- Передача начального значения модельной форме в django
- Как создать Django ModelForm с предварительно заполненными обязательными полями?
- Установка начального значения в форме django model
Основная причина, по которой это не сработает, заключается в том, что 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