Добавление продуктов в корзину не работает должным образом - PullRequest
4 голосов
/ 08 мая 2020

У меня проблема с кодом, более конкретная ошибка c, то есть когда я добавляю товар в корзину впервые (с вариациями, скажем, Зеленый и Средний), это добавляется красиво. Затем, когда я добавляю еще один элемент (скажем, синий и маленький), он также работает. Но когда я увеличиваю количество элемента с order_summary.html, он увеличивает количество другого элемента, а не того, который я щелкнул (если я щелкнул красный и средний, количество синих и больших увеличивается) и говорит: Пожалуйста, укажите требуемые вариации. Почему это происходит? Также стоит отметить, что когда я добавляю один и тот же элемент с такими же вариациями со страницы моего отдельного продукта, он работает нормально. Я думаю, что эта ошибка возникает из-за того, как написаны мои взгляды. Сам пытался решить, но теряюсь. Кто-нибудь может мне помочь? Заранее спасибо!

My models.py:

class Item(models.Model):
   title = models.CharField(max_length=120)
   price = models.FloatField()

class Variation(models.Model):
   item = models.ForeignKey(Item, on_delete=models.CASCADE)
   name = models.CharField(max_length=50) # size, color

class ItemVariation(models.Model):
   variation  = models.ForeignKey(Variation, on_delete=models.CASCADE)
   value = models.CharField(max_length=50) # small, medium large etc

class OrderItem(models.Model):
   user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
   item  = models.ForeignKey(Item, on_delete=models.CASCADE)
   item_variations = models.ManyToManyField(ItemVariation)
   quantity = models.IntegerField(default=1)
   ordered = models.BooleanField(default=False)

class Order(models.Model):
   user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
   ref_code = models.CharField(max_length=20)
   ordered = models.BooleanField(default=False)
   items = models.ManyToManyField(OrderItem)
   start_date = models.DateTimeField(auto_now_add= True)
   ordered_date = models.DateTimeField()

My views.py:

@login_required
def add_to_cart(request, slug):
    item = get_object_or_404(Item, slug=slug)
    variations = request.POST.getlist('variations', [])
    print(variations)
    print(request.POST)
    minimum_variation_count = Variation.objects.filter(item=item).count()
    print(minimum_variation_count)
    print(len(variations))
    if len(variations) < minimum_variation_count:
        messages.info(request, "Please specify the required variations.")


    order_item_qs = OrderItem.objects.filter(
        item=item,
        user= request.user,
        ordered=False,
    )

    for v in variations:
        print(v)
        order_item_qs = order_item_qs.filter(
            item_variations__value=v
        )

    if order_item_qs.exists():
        order_item = order_item_qs.first()
        order_item.quantity += 1
        # messages.success(request, "Product quantity was updated.")
        order_item.save()
    else:
        order_item = OrderItem.objects.create(
            item=item,
            user= request.user,
            ordered=False,
        )

        # order_item.item_variations.add(*variations)
        item_variations_to_add = ItemVariation.objects.filter(
                variation__item=item,
                value__in=variations
            ).values_list('id', flat=True)
        order_item.item_variations.add(*item_variations_to_add)
        order_item.save()

    order_qs = Order.objects.filter(user=request.user, ordered=False)
    if order_qs.exists():
        order = order_qs[0]
        if not order.items.filter(item__id=order_item.id).exists():
            order.items.add(order_item)
            # print(request.POST.getlist('variations', None)) 
            messages.success(request, "Product added to cart.")
            return redirect("order-summary")
    else:
        ordered_date = timezone.now()
        order = Order.objects.create(user=request.user, ordered_date=ordered_date)
        order.items.add(order_item)
        # print(request.POST.getlist('variations', None)) 
        messages.success(request, "Product added to cart.")
        return redirect("order-summary")
    return redirect("order-summary")


@login_required
def remove_single_item_from_cart(request, slug):
    item = get_object_or_404(Item, slug=slug)
    variations = request.POST.getlist('variations', [])
    order_qs = Order.objects.filter(
        user=request.user,
        ordered=False
    )
    if order_qs.exists():
        order = order_qs[0]
        # check if the order item is in the order
        if order.items.filter(item__slug=item.slug).exists():
            order_item = OrderItem.objects.filter(
                item=item,
                user=request.user,
                ordered=False
            )[0]
            if order_item.quantity > 1:
                order_item.quantity -= 1
                order_item.save()
                messages.info(request, "Product quantity was updated.")
            else:
                order.items.remove(order_item)
                order_item.delete()
                messages.info(request, "Product was removed from cart.")
            return redirect("order-summary")
        else:
            messages.info(request, "Product was not in your cart")
            return redirect("product", slug=slug)
    else:
        messages.info(request, "You do not have an active order")
        return redirect("product", slug=slug)
    return redirect("order-summary")

My order_summary. html (где я могу увеличить количество ):


<th scope="row" class="border-0">
  <div class="p-2">
    <img src="{{ order_item.item.image_url }}" alt="" width="70" class="img-fluid rounded shadow-sm">
      <div class="ml-3 d-inline-block align-middle">
        <h5 class="mb-1"> <a href="{{ order_item.item.get_absolute_url }}" class="text-dark
        -inline-block align-middle">{{ order_item.item.title }}</a>
        {% for var in order_item.item_variations.all %}
          <!-- <h6>{{ order_item.item_variations.all }}</h6> -->
          <!-- <ul> -->
            <!-- <li><h6>{{ var.variation.name }}: {{ var.value }}</h6></li> -->
          <!-- </ul> -->
          <h6>{{ var.variation.name }}: {{ var.value }}</h6>
        {% endfor %}
      </div>
  </div>
</th>
{% if order_item.item.discount_price %}
  <td class="border-0 align-middle"><strong>${{ order_item.get_total_discount_item_price }} <span class="badge badge-warning">Saving ${{ order_item.get_amount_saved }}</span></strong></td>
{% else %}
  <td class="border-0 align-middle"><strong>${{ order_item.get_total_item_price }}</strong></td>
{% endif %}
    <td class="border-0 align-middle"><strong>
      <div class="pull-center">
          <a href="{% url 'remove-single-item-from-cart' order_item.item.slug %}" class="btn mr-2"><i class="fa fa-minus"></i></a>
          {{ order_item.quantity }}<a href="{% url 'add-to-cart' order_item.item.slug %}" class="btn ml-2"><i class="fa fa-plus"></i></a>
        </div>      
      </div>
      </strong></td>
    <td class="border-0 align-middle"><a href="{% url 'remove-from-cart' order_item.item.slug %}" class="text-dark"><i class="fa fa-trash"></i></a></td>
  </tr>

My single_product. html (отсюда я могу увеличить количество своего продукта, и он работает правильно):

<form class="form" method="POST" action="{{ object.get_add_to_cart_url }}">
                    {% csrf_token %}
                      {% for var in object.variation_set.all %}
                        <h5>Choose {{ var.name }}</h5>
                        <select class="form-control mb-4 col-md-4" name="variations">
                          {% for item in var.itemvariation_set.all %}
                            <option value="{{ item.value }}">{{ item.value|capfirst }}</option>
                          {% endfor %}
                        </select> 
                      {% endfor %}


                    <div class="action">
                      <button class="btn btn-success">Add to Cart</button>
                      <button class="like btn btn-danger" type="button"><span class="fa fa-heart"></span></button>
                    </div>
                  </form>

My admin.py:

from django.contrib import admin
from .models import Item, Variation, ItemVariation


class ItemVariationAdmin(admin.ModelAdmin):
    list_display = ['variation',
                    'value']

    list_filter = ['variation', 'variation__item']
    search_fields = ['value']


class ItemVariationInLineAdmin(admin.TabularInline):
    model = ItemVariation
    extra = 1


class VariationAdmin(admin.ModelAdmin):
    list_display = ['item',
                    'name']
    list_filter = ['item']
    search_fields = ['name']
    inlines = [ItemVariationInLineAdmin]

admin.site.register(Item)
admin.site.register(ItemVariation, ItemVariationAdmin)
admin.site.register(Variation, VariationAdmin)

My urls.py:

path('add_to_cart/<slug>/', orders_views.add_to_cart, name='add-to-cart'),
path('remove_from_cart/<slug>/', orders_views.remove_from_cart, name='remove-from-cart'),

Ответы [ 5 ]

3 голосов
/ 11 мая 2020
  1. Я ожидал бы, что variations будет пустым, поскольку вы не отправляете через запрос POST, а также не отправляете какие-либо варианты ввода из вашей формы html. Вот почему вы получаете сообщение "Please specify the required variations.". (len(variations) == 0)
  2. Поскольку variations пуст, for v in variations не выполняет никакой фильтрации. Таким образом, когда вы дойдете до order_item_qs.first(), вы не обязательно получите ожидаемый OrderItem, а +=1 может оказаться на неправильном OrderItem.

Чтобы исправить:

  1. Проще всего было бы добавить конечную точку URL-адреса increase_quantity, где вы передаете идентификатор OrderItem в представление. Тогда вы точно знаете, какой OrderItem нужно увеличить количество, и вам не нужно будет выполнять какую-либо фильтрацию, чтобы найти его.
  2. В противном случае вам нужно выяснить, как вы хотите отправлять варианты в представление. Если вы используете POST, вы хотите использовать какой-либо ввод. Если GET, вы можете добавить URL-адрес или что-то в этом роде. (Я настоятельно рекомендую отправлять как POST-запрос.)
2 голосов
/ 16 мая 2020

Вы упомянули в комментариях, что теперь вам нужны решения как для увеличения, так и для уменьшения количества OrderItem. Пора применить другой подход.

Это решение обеспечивает представление, которое учитывает изменения количества заказанных товаров (плюс и минус). Он использует первичный ключ OrderItem, который намного надежнее, чем комбинация Item.pk и его вариантов.

Добавьте это в views.py:

@login_required
def update_qty(request):
    if request.method == 'POST':
        item_slug = request.POST.get('item_slug', None)
        # Check for an order_item
        order_item_pk = request.POST.get('order_item', None)
        order_item = OrderItem.objects.filter(pk=order_item_pk).first()
        if not order_item:
            messages.info(request, "Product was not in your cart")
            return redirect("product", slug=item_slug)
        # Check for an active order
        order = Order.objects.filter(user=request.user, ordered=False).first()
        if not order:
            messages.info(request, "You do not have an active order")
            return redirect("product", slug=item_slug)
        # Check that order_item is in active order
        if not order_item.order == order:
            messages.info(request, "Product was not in your cart")
            return redirect("product", slug=slug)
        # Update quantities 
        action = request.POST.get('action', None)
        if action == "plus":
            order_item.quantity += 1
            order_item.save()
            messages.info(request, "Product quantity was updated.")
        elif action == "minus":
            order_item.quantity -= 1
            if order_item.quantity < 1:
                order_item.delete()
                messages.info(request, "Product was removed from cart.")
            else
                order_item.save()
                messages.info(request, "Product quantity was updated.")
    return redirect("order-summary")

Добавьте это в свой urls.py:

path('update-qty', orders_views.update_qty, name='update-qty'),

В order_summary.html, внизу, замените это:

<div class="pull-center">
    <a href="{% url 'remove-single-item-from-cart' order_item.item.slug %}" class="btn mr-2"><i class="fa fa-minus"></i></a>
    {{ order_item.quantity }}<a href="{% url 'add-to-cart' order_item.item.slug %}" class="btn ml-2"><i class="fa fa-plus"></i></a>
    </div>
</div>

на что-то вроде этого:

<div class="pull-center">
    <form method="POST" action="{% url 'update-qty' %}">
        {% csrf_token %}
        <button type="submit" name="action" value="minus" class="btn mr-2"><i class="fa fa-minus"></i></button>
        {{ order_item.quantity }}<button type="submit" name="action" value="plus" class="btn ml-2"><i class="fa fa-plus"></i></button>
        <input type="hidden" name="item_slug" value="{{ order_item.item.slug }}">
        <input type="hidden" name="order_item" value="{{ order_item.pk }}">
    </form>
</div>
2 голосов
/ 16 мая 2020

Если вы не хотите менять свои представления, вашему order_summary.html нужна форма для каждого OrderItem, чтобы отправить соответствующие варианты при изменении количества.

В order_summary.html, внизу, замените это:

<div class="pull-center">
    <a href="{% url 'remove-single-item-from-cart' order_item.item.slug %}" class="btn mr-2"><i class="fa fa-minus"></i></a>
    {{ order_item.quantity }}<a href="{% url 'add-to-cart' order_item.item.slug %}" class="btn ml-2"><i class="fa fa-plus"></i></a>
    </div>
</div>

на что-то вроде этого:

<div class="pull-center">
    <form method="POST" action="{% url 'add-to-cart' order_item.item.slug %}">
        {% csrf_token %}
        <a href="{% url 'remove-single-item-from-cart' order_item.item.slug %}" class="btn mr-2"><i class="fa fa-minus"></i></a>
        {{ order_item.quantity }}<button type="submit" class="btn ml-2"><i class="fa fa-plus"></i></button>
        {% for iv in order_item.item_variations.all %}
        <input type="hidden" name="variations" value="{{ iv.value }}">
        {% endfor %}
    </form>
</div>

Вам нужно будет убедиться, что этот раздел вашего шаблона еще не завернут в форму.

1 голос
/ 12 мая 2020

Думаю, ваша проблема находится в представлении

@login_required
def add_to_cart(request, slug):
    item = get_object_or_404(Item, slug=slug)

    order_item_qs = OrderItem.objects.filter(
        item=item,
        user=request.user,
        ordered=False
    )

    item_var = []  # item variation
    if request.method == 'POST':
        for items in request.POST:
            key = items
            val = request.POST[key]
            try:
                v = Variation.objects.get(
                    item=item,
                    category__iexact=key,
                    title__iexact=val
                )
                item_var.append(v)
            except:
                pass

        if len(item_var) > 0:
            for items in item_var:
                order_item_qs = order_item_qs.filter(
                    variation__exact=items,
                )

    if order_item_qs.exists():
        order_item = order_item_qs.first()
        order_item.quantity += 1
        order_item.save()
    else:
        order_item = OrderItem.objects.create(
            item=item,
            user=request.user,
            ordered=False
        )
        order_item.variation.add(*item_var)
        order_item.save()

    order_qs = Order.objects.filter(user=request.user, ordered=False)
    if order_qs.exists():
        order = order_qs[0]
        # check if the order item is in the order
        if not order.items.filter(item__id=order_item.id).exists():
            order.items.add(order_item)
            messages.info(request, "This item quantity was updated.")
            return redirect("order-summary")
    else:
        ordered_date = timezone.now()
        order = Order.objects.create(
            user=request.user, ordered_date=ordered_date)
        order.items.add(order_item)
        messages.info(request, "This item was added to cart.")
        return redirect("order-summary")

Вместо варианта элемента я бы использовал go для Менеджера вариантов

class VariationManager(models.Manager):
    def all(self):
        return super(VariationManager, self).filter(active=True)

    def sizes(self):
        return self.all().filter(category='size')

    def colors(self):
        return self.all().filter(category='color')


VAR_CATEGORIES = (
    ('size', 'size',),
    ('color', 'color',),
)


class Variation(models.Model):
    item = models.ForeignKey(Item, on_delete=models.CASCADE)
    category = models.CharField(
        max_length=120, choices=VAR_CATEGORIES, default='size')
    title = models.CharField(max_length=120)

    objects = VariationManager()
    active = models.BooleanField(default=True)

    def __str__(self):
        return self.title

для шаблонов

                {% if order_item.variation.all %}
                {% for variation in order_item.variation.all %}
                {{ variation.title|capfirst }}
                {% endfor %}
                {% endif %}

Это должно работать

0 голосов
/ 14 мая 2020

Я бы предпочел использовать либо представления на основе классов [общие], либо представления на основе классов для представлений CRUD в любом коде приложения. представления на основе функций - это слишком много для простых представлений на основе CRUD.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...