【问题标题】:How to display only the first occurrence of an item in a set on a Django template?如何在 Django 模板的集合中只显示第一次出现的项目?
【发布时间】:2020-01-27 18:19:06
【问题描述】:

我正在学习 Django,所以这可能不是做我想做的最好的方式。

我在这里要做的是显示一个在购物车中被多次选择的项目,只显示一次(以及它旁边被选择的次数)

我整天都在寻找一种方法,但我设法做的最接近的事情是在视图内部生成两个集合:single_items 和 multiple_items。

显示只选择一次的项目没有问题,但是当涉及多次选择的项目时,我只能显示它们n次,旁边是出现的次数,如图所示

虽然我宁愿使用 python 代码(在 views.py 中)进行所有计算,但使用 Django 模板语言的解决方案会对我有用。

请考虑我需要在 url 中传递项目 ID 才能使删除链接起作用。

这里是views.py

    from django.shortcuts import render, redirect
    from .models import Cart, Item, CartItem
    from django.db.models import Sum


    # Create your views here.
    def home(request):
        items = Item.objects.all()
        carts = Cart.objects.all()
        length = len(Cart.objects.all())
        cart = carts[length - 1]
        cart_items = cart.items.all()
        total = cart_items.aggregate(Sum('price'))['price__sum']
        if total is None:
            total = 0
        number_of_items = cart_items.count()
        deletable_items = CartItem.objects.all()

        occurrences = None
        single_items = set()
        multiple_items = set()
        for deletable_item in deletable_items:
            occurrences = deletable_items.filter(item__name=deletable_item.item).count()
            if occurrences > 1:
                deletable_item.occurrences = occurrences
                multiple_items.add(deletable_item)
            elif occurrences == 1:
                deletable_item.occurrences = occurrences
                single_items.add(deletable_item)

        return render(request, 'cart/home.html', {'cart': cart,
                                                  'items': items,
                                                  'cart_items': cart_items,
                                                  'total': total,
                                                  'number_of_items': number_of_items,
                                                  'deletable_items': deletable_items,
                                                  'multiple_items': multiple_items,
                                                  'single_items': single_items,
                                                  'occurrences': occurrences
                                                  })


    def add_to_cart(request, item_id):
        item_id = Item.objects.get(id=item_id)
        carts = Cart.objects.all()
        length = len(Cart.objects.all())
        cart = carts[length - 1]
        cart_item = CartItem.objects.create(item=item_id, cart=cart)
        return redirect(home)


    def remove_from_cart(request, item_id):
        item_to_remove = CartItem.objects.get(id=item_id)
        item_to_remove.delete(

)
    return redirect(home)

还有我的模板

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Home page</title>
</head>
<body>
<h1>My Restaurant</h1>
<div><h1>Menu</h1></div>
{% for item in items %}
<ul>{{ item }} £{{ item.price}} <a href="{% url 'add_to_cart' item.id %}">Add</a></ul>
{% endfor %}
<div><h1>Order</h1></div>
{{ cart }}
<br>
<h2>items selected: {{ number_of_items }}</h2>
{% for single_item in single_items %}
<ul>{{ single_item }} x {{ single_item.occurrences }} <a href="{% url 'remove_from_cart' single_item.id %}">Remove</a></ul>
{% endfor %}
{% for multiple_item in multiple_items %}
<ul>{{ multiple_item }} x {{ multiple_item.occurrences }} <a href="{% url 'remove_from_cart' multiple_item.id %}">Remove</a></ul>
{% endfor %}
<h2>Total</h2>
{{ total|floatformat:2 }}
</body>
</html>

以防万一模型

from django.db import models
from django.contrib.auth.models import User


# Create your models here.
class Item(models.Model):
    name = models.CharField(max_length=25)
    price = models.DecimalField(max_digits=5, decimal_places=2)

    def __str__(self):
        return self.name


class Cart(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)
    items = models.ManyToManyField(Item, through='CartItem')

    def __str__(self):
        return 'Order number: %s' % self.id


class CartItem(models.Model):
    item = models.ForeignKey(Item, on_delete=models.CASCADE)
    cart = models.ForeignKey(Cart, on_delete=models.CASCADE)

    def __str__(self):
        return str(self.item)

编辑

目前我正在使用 Willem Van Onsem 建议的这个 view.py

from django.shortcuts import render, redirect
from .models import Cart, Item, CartItem
from django.db.models import Sum, Count, F


# Create your views here.
def home(request):
    cart = Cart.objects.filter(user=request.user).last()
    items = Item.objects.all()
    cart_items = Item.objects.filter(
        cartitem__cart=cart
    ).annotate(
        ncount=Count('cartitem')
    )
    total = cart_items.aggregate(total=Sum(F('price') * F(float('ncount'))))['total']
    context = {
        'items': items,
        'total': total,
        'cart_items': cart_items
    }
    return render(request, 'cart/home.html', context)


def add_to_cart(request, item_id):
    item_id = Item.objects.get(id=item_id)
    carts = Cart.objects.all()
    length = len(Cart.objects.all())
    cart = carts[length - 1]
    cart_item = CartItem.objects.create(item=item_id, cart=cart)
    return redirect(home)


def remove_from_cart(request, item_id):
    item_to_remove = CartItem.objects.get(id=item_id)
    item_to_remove.delete()
    return redirect(home)

【问题讨论】:

  • 嗯,问题是你每次都计算项目的数量。但这些是不同 CartItems,因此set(..) 确实不将它们视为不同的。
  • 我想你想说 set() 确实将它们视为不同的,这就是允许重复的原因(它们是具有相同名称属性的不同对象)
  • carts[length-1] 在做什么?如果有多个用户,他们将看到最后一个购物车,因此一个用户将看到另一个用户的购物车。
  • 因为两个名字都以Mirko 为名的人,不本质上是同一个人。
  • 只是为了获取创建的最后一个购物车(订单)...

标签: django django-templates django-views


【解决方案1】:

如果您甚至在用户签出之前将所选项目保存在数据库中,我不建议这样做,那么您可以在模型中进行类似的操作

class Product(models.Model):
    #model info


class Order(models.Model):
    item = modelS.ForeignKey(Product, on_delete=models.CASCADE)
    user = modelS.ForeignKey(User, on_delete=models.CASCADE)
    times = models.IntagerField(default=1)
    #..... other relevant fields

class Cart(models.Model):
    orders = models.ManyToManyField(Order)
    user = modelS.ForeignKey(User,related_name='cart_user' on_delete=models.CASCADE)
    #other details like date etc....

然后是一个检查模型是否已经存在的视图

def add_order(request,id):
    user = request.user
    item = Product.objects.get(id=id)
    try:
       order = Order.objects.get(item=item, user=user)
       order.times+=1
       order.save()
    except:
      order = Order.objects.create(
             item = item,
             user = user
            )
     try:
         my_cart = Cart.objects.get(user=user)
     except:
         my_cart = Cart.objects.create(user=user)
     my_cart.orders.add(order)

   #return http reeponse with the new data

我建议你使用ajax提交请求到调用视图更新的url,以便以后减少页面刷新

【讨论】:

  • 谢谢kim,我也会检查你的答案。正如我所说的,我只是在学习 Django,而且我对 Python 的了解程度还不错。我试图建立一个让事情最小化的项目。当然我会遇到 Ajax、Javascript 和 Django 会话,但目前我宁愿做这些事情尽可能简单。
  • 谢谢,肯定会很长:)
  • 不多,一旦你开始得到这些东西你就会知道它们很简单
【解决方案2】:

我认为在这里开发两个查询集可能会更好:

from django.db.models import Count, F, Sum

def home(request):
    cart = Cart.objects.filter(user=request.user).last()
    items = Item.objects.all()
    cart_items = Item.objects.filter(
        cartitem__cart=cart
    ).annotate(
        ncount=Count('cartitem', output_field=DecimalField(max_digits=5, decimal_places=0))
    )
    total = cart_items.aggregate(total=Sum(F('price') * F('ncount')))['total']
    context = {
        'items': items,
        'total': total,
        'cart_items': cart_items
    }
    return return render(request, 'cart/home.html', context)

在您的模板中,您可以使用以下内容渲染购物车:

<h2>items selected: {{ cart_items|length }}</h2>
{% for item in cart_items %}
    <ul>{{ item }} x {{ item.ncount }} <a href="{% url 'remove_from_cart' item.id %}">Remove</a></ul>
{% endfor %}
<h2>Total</h2>
{{ total|floatformat:2 }}

所以没有理由区分购买一次或多次的物品。

您可以通过以下方式实现删除功能:

def remove_from_cart(request, item_id):
    cart = Cart.objects.filter(user=request.user).last()
    item_to_remove = CartItem.objects.filter(
        item_id=item_id,
        cart=cart
    ).delete()
    return redirect(home)

【讨论】:

  • 哇,这要短得多:) 我正在尝试您的解决方案(并且将研究更多关于查询的内容,因为我还没有达到这个级别)但它返回一个关于“ncount”的错误。我也尝试将其更改为“计数”,但似乎我必须在模型中创建一个属性“计数”才能使其工作。对吗?
  • @MirkoOricci:对不起,我打错了,应该是cart_items.aggregate(..)。
  • 是的,似乎更好,但它返回'表达式包含混合类型:DecimalField,IntegerField。您必须设置 output_field。现在。我想我得想办法把'ncount'转换成十进制,对吧?
  • @MirkoOricci:是的,应该类似于Cast(..)。
  • mmm...如果我尝试将 price 和 total 转换为整数,它表示这是不可能的,因为它们以 10 为底,如果我尝试将 ncount 转换为浮点数,它说它是无法将字符串解析为浮点数...
【解决方案3】:

只是另一种方法:为什么不在 models.py 中为 CartItem 模型添加一个名为“数量”或类似名称的字段?例如:

class CartItem(models.Model):
    item = models.ForeignKey(Item, on_delete=models.CASCADE)
    cart = models.ForeignKey(Cart, on_delete=models.CASCADE)
    quantity = models.IntegerField(default=0)

然后在您的 add_to_cart 视图中,您会这样做:

def add_to_cart(request, item_id):
    item = Item.objects.get(id=item_id)
    cart = Cart.objects.filter(user=request.user).last()
    cart_item_qs = CartItem.objects.filter(cart__pk=cart.pk)
    if cart_item_qs.exists():
        cart_item_qs.update(quantity=F('quantity') + 1)
    else:
        cart.items.add(item)
        cart.save()

    return redirect(home)

之后,您可以在模板中将 CartItem 模型的实例传递给上下文数据,从而从那里访问数量字段值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-18
    • 2014-07-30
    相关资源
    最近更新 更多