Résolue

Django : erreur si deux utilisateur mettent un même élément dans le panier

# Stripe # Bases de données # Django

Gabriel Trouvé

Mentor

Bonsoir et merci d'avance car ma question fait 3km de long lol.

Je viens à la fois présenter une idée et demander conseil.

Ici j'empêche qu'un utilisateur mette deux fois la même idée sans son panier dans un premier temps.

Je vérifie aussi que l'idée ne soit pas dans le panier d'un autre utilisateur en bouclant sur tous les paniers.

La méthode

j'ai testé ça fonctionne.
ça parait être une bonne méthode ?

Comment supprimer un panier avec le temps

La suite c'est que je voudrais que si le panier est créé, mais que si on ne va jamais jusqu'au paiement, qu'au bout d'un certains temps le panier soit supprimé. Niveau ressources je n'ai pas trouvé énormément de choses... Ou je cherche mal peut-être aussi (pas les bons mots clés).

EDIT : J'ai trouvé ce package https://pypi.org/project/django-apscheduler/ Je ne sais pas si'il y a une solution toute bête (j'avais pensé au manager) ou plus simple sinon j'utiliserai ce package.

Mon code pour la vue ajouter au panier :

def add_to_cart(request, slug):
    user = request.user

    user_cart, _ = Cart.objects.get_or_create(buyer=user)
    idea = get_object_or_404(Idea, slug=slug)

    if request.method == "POST":

        if idea in user_cart.ideas.all():
            # On ne peut pas ajouter deux fois la même idée
            messages.add_message(request, messages.ERROR, "L'idée est déjà dans le panier")
            return redirect("ideas:all")

        for cart in Cart.objects.all():
            # Je vérifie que l'idée ne soit pas dans le panier d'un autre
            if idea in cart.ideas.all():
                messages.add_message(request, messages.ERROR, "L'idée est déjà dans le panier de quelqu'un.")
                if not user_cart.ideas.all():
                    # Je supprime le panier créé s'il est vide
                    user_cart.delete()
                return redirect("ideas:all")
        else:
            user_cart.ideas.add(idea)
            # On ajoute au panier et on va dans la vue panier

            return redirect("ideas:cart")

Autre question qui peut aller avec.

Concernant la méthode cart_paid. Je l'appelle depuis le webhook stripe.
Ma question c'est :
est-ce qu'il est vraiment utile de faire un self.ideas.clear() avant de faire le self.delete() ? Ou je peux faire un self.delete() direct ?

Car dans la BDD la table qui gèrel a relation many2many est supprimée si je supprime l'instance Cart dans tous les cas ?

Ici ce sont les idées qui sont dans le panier.

Mon modèle panier :

class Cart(models.Model):
    buyer = models.OneToOneField(AUTH_USER_MODEL, on_delete=models.CASCADE, verbose_name="Acheteur")
    ideas = models.ManyToManyField(Idea, verbose_name="Idées")

    def total_cart(self):
        total = 0
        for idea in self.ideas.all():
            total += idea.price

        return total

    def cart_paid(self, user):
        for idea in self.ideas.all():
            idea.paid = True
            idea.buyer = user
            idea.ordered_date = timezone.now()
            idea.save()

        self.ideas.clear()
        self.delete()

Merci d'avance

Salut Gab !

Concernant ta méthode pour ajouter au panier, ça fonctionne mais tu pourrais simplifier un peu le code en fusionnant les conditions et avec une meilleure utilisation du langage de requête (et en utilisant une "guard clause" pour éviter trop de structures imbriquées quand tu vérifies le type de la méthode) :

def add_to_cart(request, slug):
    user = request.user
    user_cart, _ = Cart.objects.get_or_create(buyer=user)
    idea = get_object_or_404(Idea, slug=slug)

    if request.method != "POST":
        raise Http404("Requête invalide")

    if user_cart.ideas.filter(id=idea.id).exists() or Cart.objects.filter(ideas__id=idea.id).exists():
        messages.add_message(request, messages.ERROR, "L'idée est déjà dans le panier")
        if not user_cart.ideas.exists():
            user_cart.delete()  # Supprime le panier créé s'il est vide
        return redirect("ideas:all")
    else:
        user_cart.ideas.add(idea)  # Ajoute l'idée au panier de l'utilisateur
        return redirect("ideas:cart")

Pour le app scheduler tu pourrais utiliser Celery sur une app plus poussée mais pour une plus petite application ça peut être plus simple d'utiliser app scheduler même si je ne l'ai jamais utilisé. Tu peux aussi utiliser des tâches cron (cronjob).

Pour ta question sur cart_paid, effectivement, il n'est pas nécessaire de faire self.ideas.clear() avant de faire self.delete(). Lorsque tu supprimes une instance de Cart, Django supprime automatiquement les relations ManyToMany associées. Donc tu peux simplement faire self.delete() :)

Gabriel Trouvé

Mentor

Merci THibault !

Je n'avais jamais pensé à la guard clause.
ça fait bizarre de voir la vue comme ça du coup je n'ai pas l'habitude. Va falloir que je me force ^^
j'ai mes habitudes de débutant à tout détailler lol

C'est quelque chose qu'on fait très souvent, quand tu as des figures du genre que tu souhaites "évacuer", tu les mets au début en inversant ta condition comme ça tu règles déjà les cas de figure dans lesquels tu as juste un return et après tu peux avoir tout directement dans le corps de ta fonction sans avoir dès le départ plusieurs niveaux d'indentation :)

Du coup dès mes prochains projets je vais appliquer ce principe ça evite les vues à rallonge lol

Et en plus dans cas ci je ne veux vraiment qu'une requête post... Je n'y avais pas pensé ^^

Merci ! :)

Inscris-toi

(c'est gratuit !)

Inscris-toi

Tu dois créer un compte pour participer aux discussions.

Créer un compte

Rechercher sur le site

Formulaire de contact

Inscris-toi à Docstring

Pour commencer ton apprentissage.

Tu as déjà un compte ? Connecte-toi.