Partage
  • Partager sur Facebook
  • Partager sur Twitter

Error "ValueErro..." django, formset_factory

    18 juin 2023 à 17:30:58

    Bonjour à tous !
    J'espère que vous allez tous bien, je suis ici pour une erreur :  Error "ValueError: The 'image' attribute has no file associated with it" lors de la création d'annonce avec plusieurs images en utilisant Django et formset_factory

    Je suis en train de créer une plateforme django, celle-ci possède une fonctionnalité de création d'annonces. Dans la page de création d'annonce il y a un formulaire (AnnonceForm), avec comme champ : titre, description, image, et adresse. Ce formulaire est basé sur un modèle Offre. Ce dont j'ai besoin c'est de permettre à l'utilisateur dans la page de création d'annonces d'upload plusieurs images. Mais comme je ne sais pas comment faire cela avec seulement 1 formulaire, alors j'ai décidé de créer une 2ème formulaire (PhotoForm) pour gérer plusieurs images avec formset_factory. Pour résumé, l'idée est de gérer tous les champs : titre, description, adresse sauf le champ image avec le formulaire AnnonceForm, et de gérer le formset_factory du champ image avec le formulaire PhotoForm. Et c'est ce que j'ai fait. Mais lorsque j'appuie sur le bouton valider de la page de création d'annonces je suis redirigé vers une page d'erreur avec ce message "ValueError at /offre/
    The 'image' attribute has no file associated with it."

    Voici le code :
    le template offre_page_form :
    {% extends 'base.html' %}
    
    {% block content %}
    <h1>La page de création d'annonce</h1>
        <div class="form_upload">
    <form method="post" enctype="multipart/form-data">
        {{ formset.management_form }}
        {% csrf_token %}
        {{ form.as_p }}
    
        {% for form in formset %}
            {{ form.as_p }}
            {% endfor %}
       <input type="submit" class="valid_annonce"value="publier" name="publier"/>
    </form>
        </div>
    {% endblock content %}


    la view Annoncepage :
    @login_required
    def Annonce_page(request):
        form = forms.AnnonceForm()
        reservation = Reservation()
        PhotoFormset = formset_factory(forms.PhotoForm, extra=5)
        formset = PhotoFormset()
    
        if request.method == 'POST':
            form = forms.AnnonceForm(request.POST, request.FILES)
            formset = PhotoFormset(request.POST, request.FILES)
            if form.is_valid():
    
                filephoto = form.save(commit=False)
                filephoto.uploader = request.user
    
                result = geocoder.geocode(filephoto.ville)
                print("\n\n\nSolution de result\n\n\n", result)
                if result:
                    filephoto.latitude = lat = result[0]['geometry']['lat']
                    filephoto.longitude = lng = result[0]['geometry']['lng']
                    filephoto.save()
                    print("Latitude", filephoto.latitude)
                    print("Longitude", filephoto.longitude)
                else:
                    print('Erreur cela ne fonctionne pas actuellement ')
    
                reservation.caretaker = request.user
                if formset.is_valid():
                    for form in formset:
                        if form.cleaned_data:
                            photo=form.save(commit=False)
                            photo.uploader = request.user
                            photo.save()
    
    
    
                return redirect('home')
        return render(request, 'blog/offre_page_form.html', context={'form': form, 'formset': formset})

    le modèle Offre

    class Offre(models.Model):
        titre = models.CharField(max_length=30)
        image = models.ImageField(verbose_name="Image d'annonce", blank=True, null=True)
        description = models.TextField(max_length=1000)
        reservation = models.BooleanField(default=False, verbose_name="reservation_offre")
        alert = models.CharField(max_length=200, null = True)
        user_accept = models.BooleanField(default=None, verbose_name="Accept", null=True)
        user_refuse = models.BooleanField(default=None, verbose_name="Refuse", null=True)
        uploader = models.ForeignKey(User, on_delete=models.CASCADE, related_name="annonces", null=True, blank=True)
        ville = models.CharField(max_length=50)
        latitude = models.FloatField()
        longitude = models.FloatField()
    
    
    
        
        IMAGE_MAX_SIZE = (800, 800)
    
        def resize_image(self):
            image= Image.open(self.image)
            image.thumbnail(self.IMAGE_MAX_SIZE)
            # sauvegarde de l'image redimensionnée dans le système de fichiers
            # ce n'est pas la méthode save() du modèle !
            image.save(self.image.path)
    
        def save(self, *args, **kwargs):
            super().save(*args, **kwargs)
            self.resize_image()
    
    
    







    le fichier forms.py :
    ```

    class AnnonceForm(ModelForm):
    
        class Meta:
            model = Offre
            fields = [ 'titre', 'description','ville']
    
    class PhotoForm(ModelForm):
    
        class Meta:
            model = Offre
            fields = ['image',]
    ```
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    


    Je remercie infiniment ceux qui tenteront de m'aider ; )

    Si vous avez la moindre question, je suis ici ; )

    Bonne journée !

    • Partager sur Facebook
    • Partager sur Twitter
      19 juin 2023 à 15:40:34

      emplacement de l'erreur remontée ? (c'est indiqué dans le message d'erreur).
      Sinon ton modèle est mauvais, tu as toujours un champ image dans ton modèle Offre (avec les fonctions de redimensionnement)
      • Partager sur Facebook
      • Partager sur Twitter
        20 juin 2023 à 8:21:06

        Coucou, je te remercie pour ta réponse, 

        alors je n'ai pas compris la 1ère partie de ton message, tu veux que je fournisse l'emplacement de l'erreur est-ce bien cela ?

        Peut-être une image de la page d'erreur t'aidera à comprendre :

        Enfin pour que tu comprennes bien ma problématique : je suis actuellement capable de faire un formulaire dans lequel on peut mettre plusieurs images (voir photo ci dessous)

        mais ce que j'aimerai faire c'est "multiplié" le champ image, pour que cela donne quelque chose de ressemblant au plateforme comme leboncoin et airbnb (photo ci dessous)

        As-tu mieux compris ma demande ?

        En tout cas merci beaucoup pour ton aide.

        • Partager sur Facebook
        • Partager sur Twitter
          20 juin 2023 à 11:42:53

          regarde ton message d'erreur, il est très parlant.

          Exception Location : /home/ [...]/files.py, ligne 41 in _require_file 

          ça semble indiqué que le champ est indiqué en tant que require (obligatoire)

          et je réitère le fait que ton modèle n'est plus bon, vu que tu supprimes l'image du 1er formulaire (dans ce que tu as proposé initialement).

          Sinon, il faut changer l'approche cf la doc de django => https://docs.djangoproject.com/en/4.2/topics/http/file-uploads/#uploading-multiple-files 

          • Partager sur Facebook
          • Partager sur Twitter
            25 juin 2023 à 16:48:36

            D'accord, merci il me semble avoir trouvé trouvé une solution.
            • Partager sur Facebook
            • Partager sur Twitter
              26 juin 2023 à 11:57:32

              c'est toujours sympa de partager les solutions sur un forum, vu que c'est un peu le principe :p
              • Partager sur Facebook
              • Partager sur Twitter
                7 juillet 2023 à 2:08:16

                Salut !
                Sorry pour la réponse un peu (beaucoup) tardive, je n'avais pas vu ton message. Je suis tout à fait d'accord avec le fait de partager ses solutions sur le forum et je m'en excuse : ), mais il se trouve que ce que je pensais être une solution, n'en était en réalité pas une. J'ai été victime du biais du développeur xy, j'avais un problème x (le fait de ne pas pouvoir avoir plusieurs même champ photo dans un seul formulaire) donc j'y ai trouvé la solution y (utiliser formset_factory qui permet de démultiplier le nombre de formulaire) avec laquelle il y avait des bugs, et donc j'ai posé la question sur le forum pour le problème y et non celui initial x.
                La solution y ne fonctionne pas car ce que je voudrais c'est démultiplier le champ photo dans un formulaire, pour que dans une annonce il y ait plusieurs images, mais la solution y me donnait ça : créer un nouveau modelform photo, démultiplier ce modelform photo, mais pour chaque image upload cela créait une annonce différente, cela générait des tensions car pour chaque annonce créer avec une image, il n'y avait pas les autres champs nécéssaires, donc cela générait des bugs.
                J'ai fait quelques recherches sur internet, sans rien trouvé, beaucoup de réponses pour le problème y, mais pas x. J'espère avoir plus clairement expliqué la problématique. Est-ce que tu aurais quelques pistes ? Si tu as une question, un endroit que tu ne comprends pas, ce serait avec plaisir de t'expliquer le plus clairement possible.

                Merci beaucoup de ta réponse, tu m'as de nombreuses fois aidé à propos de bugs en python et django, et je te remercie infiniement.
                • Partager sur Facebook
                • Partager sur Twitter
                  7 juillet 2023 à 12:18:18

                  ton problème X c'est de pouvoir ajouter télécharger plusieurs photos depuis ton formulaire, donc il faut que ton formulaire accepte une liste/collection/... de photos, et que tu gères leur téléchargement et présence en base comme telle (en base, une table Photos, avec son identifiant unique, son nom/chemin serveur et l'id de l'annonce à laquelle elle est reliée).

                  Dit autrement, ton champ image ne peut pas être une ImageField, il faut lui associé un autre Model Image par exemple défini comme je l'ai indiqué au dessus (l'identifiant unique étant généré automatiquement, il ne fait pas parti du modèle)

                  un exemple issu de SO pour le cas de fichier (ce qui revient à ton besoin) https://stackoverflow.com/questions/69333909/use-view-form-in-django-to-upload-a-multiple-files/69334720#69334720 

                  • Partager sur Facebook
                  • Partager sur Twitter
                    2 août 2023 à 10:57:41

                    Bonjour Fred,

                    Après quelques jours de recherches notamment avec la ressource que tu m'as envoyé, je me suis rendu compte qu'avec la dernère version de django la 4.2 les choses étaient différentes pour upload plusieurs fichiers dans un seul form, et donc que la plupart des ressources sur les forums datant d'1 an ou plus était obsolète, c'est pour ça que django me générait des bugs avec ce code : https://stackoverflow.com/questions/69333909/use-view-form-in-django-to-upload-a-multiple-files/69334720#69334720

                    L'actuelle façon d'upload plusieurs fichiers dans un form est expliqué ici : https://docs.djangoproject.com/en/4.2/topics/http/file-uploads/#uploading-multiple-files

                    et cela fonctionne en utilisant le code d'exemple, c'est impressionnant ! Néammoins je reste bloqué sur 1 point : 

                    - 1 : Au lieu de toute faire avec le fichier forms.py comment intégrer ça en me basant sur un modèle, pour relier mes différentes images au modèle annonce.

                    Tout ça est très flou pour moi, j'ai essayé quelques petites choses, mais ce n'était pas bon. Il y a quelques liens très intéréssants, et surtout qui permettent de mieux comprendre certains points.

                    https://stackoverflow.com/questions/76797026/form-with-multiple-image-value-return-list-object-has-no-attribute-committed-wh

                    https://stackoverflow.com/questions/76299130/django-adding-a-field-which-allows-multiple-images

                    Voilà où j'en suis actuellement, merci de ton aide, je te et vous tiens au courant des avancements, s'il y en a comme moi qui sont sur ce problème.

                    -
                    Edité par yaruco 2 août 2023 à 15:44:37

                    • Partager sur Facebook
                    • Partager sur Twitter

                    Error "ValueErro..." django, formset_factory

                    × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                    × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                    • Editeur
                    • Markdown