I’m struggling around Django form. I’m not really comfortable with this part of the framework.
Here is the case. I have a ModelForm containing a OneToOneField. I would like to prepopulated the ModelChoice Field with the object referenced in the URL. Ex: catalogue/location/add/7 to add a location to the product with pk=7
NB: if the PK is not included, the form is just not prefilled
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)
I tried to override get_initial()
to pass the product to the form didn’t validate, ask to select a product in the list and moreover, it erase the inital value.
I also use get_form_kwargs()
to pass the product to the form init function and try to :
- attach the product to the ProductLocation instance
- set inital data in the init
- set
self.fidels['product'].initial = product
Thanks for your help
PS : some references on SO
- Pass initial value to a modelform in django
- How to instantiate a Django ModelForm with pre-filled required fields?
- Set initial value in django model form