
Bernardo Augusto García Loaiza
PreguntaTengo los siguientes modelos, con los cuales, pretendo asociar múltiples imágenes a un objeto de mi modelo
LodgingOffer
formsets
<code> class LodgingOffer(models.Model): created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE ) slug = models.SlugField(max_length=100, blank=True) offered_services = models.ManyToManyField( OfferedServices, related_name="lodgingoffers" ) # Other fields def __str__(self): return "%s" % self.ad_title def get_absolute_url(self): return reverse('host:detail', kwargs = {'slug' : self.slug }) # Create slug to Url object, friendly readable def create_slug(instance, new_slug=None): slug = slugify(instance.ad_title) if new_slug is not None: slug = new_slug qs = LodgingOffer.objects.filter(slug=slug).order_by("-id") exists = qs.exists() if exists: new_slug = "%s-%s" % (slug, qs.first().id) return create_slug(instance, new_slug=new_slug) return slug def pre_save_lodging_offer_receiver(sender, instance, *args, **kwargs): if not instance.slug: instance.slug = create_slug(instance) pre_save.connect(pre_save_lodging_offer_receiver, sender=LodgingOffer) # I get file name to save the images def get_image_filename(instance, filename): title = instance.lodging_offer.ad_title slug = slugify(title) return "lodging_offer_images/%s-%s" % (slug, filename)
El modelo en donde guardaría las múltiples imágenes asociadas a
LodgingOffer
<code> class LodgingOfferImage(models.Model): lodging_offer = models.ForeignKey(LodgingOffer) image = models.ImageField(upload_to=get_image_filename, verbose_name='Imagen',)
Los respectivos formularios de estos dos modelos son:
<code> class LodgingOfferForm(forms.ModelForm): title = "Crear oferta de alojamiento" class Meta: model = LodgingOffer fields = ('ad_title', 'country', 'city', 'address', 'lodging_offer_type', 'stars', 'check_in', 'check_out', 'offered_services', 'featured_amenities', 'room_type_offered', 'number_guest_room_type', 'bed_type', 'bathroom', 'room_information', 'image', 'room_value', 'additional_description', 'is_taked') class LodgingOfferImagesForm(forms.ModelForm): image = forms.ImageField(label='Imagen') class Meta: model = LodgingOfferImage fields = ('image', )
La idea que prentendo es que cuando cree un objeto
LodgingOffer
CreateView
Estoy sobreescribiedo el
get_context_data()
<code> class HostingOfferCreateView(SuccessMessageMixin, CreateView): model = LodgingOffer form_class = LodgingOfferForm #success_url = reverse_lazy("articles:article_list") success_message = "Oferta de alojamiento creada con éxito" def get_context_data(self, **kwargs): context = super(HostingOfferCreateView, self).get_context_data(**kwargs) ImageFormSet = modelformset_factory(LodgingOfferImage, form=LodgingOfferImagesForm, extra=3) if self.request.method == 'POST': lodging_offerForm = LodgingOfferForm(self.request.POST) formset = ImageFormSet(self.request.POST, self.request.FILES, queryset=LodgingOfferImage.objects.none()) if lodging_offerForm.is_valid() and formset.is_valid(): lodging_offer_form = lodging_offerForm.save(commit=False) lodging_offer_form.created_by = self.request.user lodging_offer_form.save() for form in formset.cleaned_data: image = form['image'] photo = LodgingOfferImage(lodging_offer=lodging_offer_form, image=image) photo.save() messages.success(self.request, "Offer images saved") #return redirect("articles:article_list") return HttpResponse(lodging_offer_form.get_asolute_url()) #return super(HostingOfferCreateView, self).get_context_data(**kwargs) else: print(lodging_offerForm.errors, formset.errors) else: lodging_offerForm = LodgingOfferForm() formset = ImageFormSet(queryset=LodgingOfferImage.objects.none()) context['lodging_offerForm'] = lodging_offerForm context['formset'] = formset return context
Se también que las validaciones de los formularios aquí hechas son realizadas tradicionalmente en el metodo
form_valid()
get_context_data()
Ignoro si esto es correcto no.
Y mi template es:
<code> {% extends 'layout.html' %} {% load staticfiles %} {% load bootstrap3 %} {% block body_content %} <form method="POST" enctype="multipart/form-data"> {% csrf_token %} {% for hidden in lodging_offerForm.hidden_fields %} {{ hidden }} {% endfor %} {% for field in lodging_offerForm %} {{ field }} <br /> {% endfor %} {{ formset.management_form }} {% for form in formset %} {{ form }} {% endfor %} <input type="submit" name="submit" value="Submit" /> </form> {% endblock %}
Cuando realizo un post a traves de mi formulario en mi navegador, obtengo este mensaje:
<code> Exception Type: IntegrityError at /host/lodging-offer/new/ Exception Value: null value in column "created_by_id" violates not-null constraint DETAIL: Failing row contains (65, Apartamento, AF, Envigado, Calle 40D Sur #3221, null, null, Hotel, 1 estrella, 2017-10-11, 2017-10-21, Habitación privada, Para 1 huésped, Cama individual, Baño privado, 2134, 67858, 2017-10-19 17:06:52.310857+00, null, apartamento-24-30-31-43-44-49-50, f, hosting-host-photos/1900-X-1080-Wallpapers-022.jpg)
Sé que la causa de esto es que en el request POST que se hace, no se esta enviando un valor al campo
created_by
LodgingOffer
Lo que pasa es que estoy intentándolo de esta manera en mi sección de código previa:
<code> if lodging_offerForm.is_valid() and formset.is_valid(): lodging_offer_form = lodging_offerForm.save(commit=False) # Enviando el valor para created_by lodging_offer_form.created_by = self.request.user lodging_offer_form.save()
Pero aparentemente no es la manera en como debo capturar el request del usuario en mi formulario instanciado
lodging_offerForm
Por el momento no he mirado como resolver la situación. ¿Cómo puedo adicionar el request del usuario al momento de grabar el formulario?

Diego Forero
La parte de validación del formulario y guardado de los datos lo debes hacer en el método post, CreateView tiene un método post y un método form_valid, el form_valid no lo puedes usar tan fácil porque tienes el formset entonces la parte de validación y guardado la puedes hacer en el método post.
class LoQueSea(CreateView): def post(self, request, *args, **kwargs): ImageFormSet = modelformset_factory(LodgingOfferImage, form=LodgingOfferImagesForm, extra=3) lodging_offerForm = LodgingOfferForm(self.request.POST) formset = ImageFormSet(self.request.POST, self.request.FILES, queryset=LodgingOfferImage.objects.none()) .....
Y quitas la parte de validar si el request es post en el get_context_data.
Puedes ver los métodos que tienen las Class Based Views en http://ccbv.co.uk/