构建游戏商店应用:从价格模型到购物车的实现
1. 创建价格列表数据模型
为了实现更改产品价格、记录价格添加时间和最后更新时间的功能,我们将在
gamestore/main/
目录下的
models.py
文件中创建一个名为
PriceList
的模型类。以下是具体代码:
class PriceList(models.Model):
added_at = models.DateTimeField(auto_now_add=True)
last_updated = models.DateTimeField(auto_now=True)
price_per_unit = models.DecimalField(max_digits=9,
decimal_places=2,
default=0)
game = models.OneToOneField(
Game,
on_delete=models.CASCADE,
primary_key=True)
def __str__(self):
return self.game.name
在这个模型中:
-
added_at
字段:使用
auto_now_add=True
,当向表中添加价格时,Django 会自动添加当前日期。
-
last_updated
字段:使用
auto_now=True
,每次更新时,Django 会设置当前日期。
-
price_per_unit
字段:定义为
DecimalField
,最多 9 位数字,2 位小数,默认为 0。
-
game
字段:使用
OneToOneField
与
Game
对象建立联系,当游戏被删除时,
PriceList
表中的相关行也会被删除,并且该字段被定义为主键。
-
__str__
方法:返回游戏的名称,这在使用 Django 管理界面更新价格时很有用。
创建完模型后,我们需要生成并应用迁移文件:
python manage.py makemigrations
python manage.py migrate
2. 创建游戏列表和详情页面
接下来,我们要创建视图和模板来在页面上显示游戏。
-
添加页面内容
:我们已经在
main/templates/main
目录下创建了
index.html
模板,但它目前没有显示任何内容。为了让页面更有趣,我们将添加两部分内容:
1. 页面顶部的一个部分,用于显示我们想要突出显示的游戏,如新店到货的新游戏、非常受欢迎的游戏或价格划算的游戏。
2. 突出显示的游戏部分之后,列出所有其他游戏。
-
创建部分视图模板
:我们将创建一个部分视图模板
games-list.html来显示游戏列表,该模板将被所有需要显示游戏列表的模板共享。在gamestore/main/templates/main/目录下创建games-list.html文件,内容如下:
{% load staticfiles %}
{% load humanize %}
<div class='game-container'>
{% for game in gameslist %}
{% if game.highlighted and highlight_games %}
<div class='item-box highlighted'>
{% else %}
<div class='item-box'>
{% endif %}
<div class='item-image'>
<img src="{% static game.image.url %}"></img>
</div>
<div class='item-info'>
<h3>{{game.name}}</h3>
<p>Release year: {{game.release_year}}</p>
<p>Developer: {{game.developer}}</p>
<p>Publisher: {{game.published_by}}</p>
{% if game.pricelist.price_per_unit %}
<p class='price'>
Price:
${{game.pricelist.price_per_unit|floatformat:2|intcomma}}
</p>
{% else %}
<p class='price'>Price: Not available</p>
{% endif %}
</div>
<a href="/cart/add/{{game.id}}" class="add-to-cart btn
btn-primary">
<i class="fa fa-shopping-cart" aria-hidden="true"></i>
Add to cart
</a>
</div>
{% endfor %}
</div>
注意,我们在页面顶部添加了
{% load humanize %}
,这是 Django 框架内置的一组模板过滤器,用于正确格式化游戏价格。为了使用这些过滤器,我们需要编辑
gamestore/gamestore
目录下的
settings.py
文件,并将
django.contrib.humanize
添加到
INSTALLED_APPS
设置中。
-
修改
index.html文件 :将gamestore/main/templates/main目录下的index.html文件内容替换为以下代码:
{% extends 'base.html' %}
{% block 'content' %}
{% if highlighted_games_list %}
<div class='panel panel-success'>
<div class='panel-heading'>
<h2 class='panel-title'><i class="fa fa-gamepad"
aria-hidden="true"></i>Highlighted games</h2>
</div>
<div class='panel-body'>
{% include 'main/games-list.html' with
gameslist=highlighted_games_list highlight_games=False%}
{% if show_more_link_highlighted %}
<p>
<a href='/games-list/highlighted/'>See more items</a>
</p>
{% endif %}
</div>
</div>
{% endif %}
{% if games_list %}
{% include 'main/games-list.html' with gameslist=games_list
highlight_games=False%}
{% if show_more_link_games %}
<p>
<a href='/games-list/all/'>See all items</a>
</p>
{% endif %}
{% endif %}
{% endblock %}
在这个代码中,我们使用
{% include %}
标签包含了部分视图模板,并传递了两个参数
gameslist
和
highlight_games
。
-
添加 CSS 代码
:编辑
gamestore/static/styles/目录下的site.css文件,添加以下 CSS 代码:
.game-container {
margin-top: 10px;
display:flex;
flex-direction: row;
flex-wrap: wrap;
}
.game-container .item-box {
flex-grow: 0;
align-self: auto;
width:339px;
margin: 0px 10px 20px 10px;
border: 1px solid #aba5a5;
padding: 10px;
background-color: #F0F0F0;
}
.game-container .item-box .add-to-cart {
margin-top: 15px;
float: right;
}
.game-container .item-box.highlighted {
background-color:#d7e7f5;
}
.game-container .item-box .item-image {
float: left;
}
.game-container .item-box .item-info {
float: left;
margin-left: 15px;
width:100%;
max-width:170px;
}
.game-container .item-box .item-info p {
margin: 0 0 3px;
}
.game-container .item-box .item-info p.price {
font-weight: bold;
margin-top: 20px;
text-transform: uppercase;
font-size: 0.9em;
}
.game-container .item-box .item-info h3 {
max-width: 150px;
word-wrap: break-word;
margin: 0px 0px 10px 0px;
}
-
修改
index视图函数 :编辑gamestore/main/目录下的views.py文件,修改index函数如下:
from .models import Game
def index(request):
max_highlighted_games = 3
max_game_list = 9
highlighted_games_list = Game.objects.get_highlighted()
games_list = Game.objects.get_not_highlighted()
show_more_link_promoted = highlighted_games_list.count() >
max_highlighted_games
show_more_link_games = games_list.count() > max_game_list
context = {
'highlighted_games_list':
highlighted_games_list[:max_highlighted_games],
'games_list': games_list[:max_game_list],
'show_more_link_games': show_more_link_games,
'show_more_link_promoted': show_more_link_promoted
}
return render(request, 'main/index.html', context)
在这个函数中,我们首先定义了要显示的每种类型游戏的数量,然后获取推广和非推广游戏,创建两个变量
show_more_link_promoted
和
show_more_link_games
来判断是否显示“查看更多”链接,最后创建上下文变量并调用
render
函数渲染模板。
-
注册模型到管理界面
:为了创建游戏,我们需要将模型注册到 Django 管理界面。编辑
admin.py文件,添加以下代码:
from django.contrib import admin
from .models import GamePlatform
from .models import Game
from .models import PriceList
admin.autodiscover()
admin.site.register(GamePlatform)
admin.site.register(Game)
admin.site.register(PriceList)
-
配置媒体文件路径
:由于我们要为游戏添加图片,需要配置 Django 保存上传图片的位置。编辑
gamestore/gamestore目录下的settings.py文件,在STATIC_DIRS设置下方添加以下代码:
MEDIA_ROOT = os.path.join(BASE_DIR, 'static')
- 创建游戏 :启动网站:
python manage.py runserver
浏览到
http://localhost:8000/admin
,使用我们创建的超级用户账户登录。在管理界面中,先添加游戏平台,然后添加游戏。在添加游戏之前,需要在
gamestore/static/images/
目录下放置一个名为
placeholder.png
的默认图片,图片大小为 130x180 效果更佳。可以通过
http://via.placeholder.com/130x180
获取合适大小的占位符图片。添加完游戏后,再次访问网站,应该可以在首页看到游戏列表。
3. 添加游戏列表视图
由于我们在首页没有显示所有游戏,需要构建页面来显示所有游戏。
-
创建 URL
:在主应用的
url.py
文件中,添加以下两个 URL 到
urlpatterns
列表中:
path(r'games-list/highlighted/', views.show_highlighted_games),
path(r'games-list/all/', views.show_all_games),
-
创建模板
:在
gamestore/main/templates/main目录下创建all_games.html文件,内容如下:
{% extends 'base.html' %}
{% block 'content' %}
<h2>Highlighted games</h2>
<hr/>
{% if games %}
{% include 'main/games-list.html' with gameslist=games
highlight_promoted=False%}
{% else %}
<div class='empty-game-list'>
<h3>There's no promoted games available at the moment</h3>
</div>
{% endif %}
{% endblock %}
再创建
highlighted.html
文件,内容如下:
{% extends 'base.html' %}
{% block 'content' %}
<h2>All games</h2>
<hr/>
{% if games %}
{% include 'main/games-list.html' with gameslist=games
highlight_games=True%}
{% else %}
<div class='empty-game-list'>
<h3>There's no promoted games available at the moment</h3>
</div>
{% endif %}
{% endblock %}
-
添加视图函数
:编辑
gamestore/main/目录下的views.py文件,添加以下两个函数:
def show_all_games(request):
games = Game.objects.all()
context = {'games': games}
return render(request, 'main/all_games.html', context)
def show_highlighted_games(request):
games = Game.objects.get_highlighted()
context = {'games': games}
return render(request, 'main/highlighted.html', context)
这两个函数分别获取所有游戏和仅推广游戏的列表,并将其传递给相应的模板进行渲染。
4. 创建购物车模型
目前我们的应用可以显示游戏,但用户无法购买游戏,因此需要实现一个购物车。我们将通过将购物车项目保存到数据库来实现购物车,而不是基于用户会话实现。
购物车的要求如下:
- 用户可以添加任意数量的商品。
- 用户应该能够更改购物车中的商品,例如更改商品数量。
- 可以移除商品。
- 应该有清空购物车的选项。
- 所有数据都应该经过验证。
- 如果拥有购物车的用户被删除,购物车及其项目也应该被删除。
以下是具体的实现步骤:
-
创建
ShoppingCartManager
类
:在
gamestore/main
目录下的
models.py
文件中添加以下代码:
from django.contrib.auth.models import User
class ShoppingCartManager(models.Manager):
def get_by_id(self, id):
return self.get(pk=id)
def get_by_user(self, user):
return self.get(user_id=user.id)
def create_cart(self, user):
new_cart = self.create(user=user)
return new_cart
这个类包含三个方法:
-
get_by_id
:根据 ID 获取购物车。
-
get_by_user
:根据用户实例获取购物车。
-
create_cart
:当用户创建账户时调用,创建一个新的购物车。
-
创建
ShoppingCart类 :
class ShoppingCart(models.Model):
user = models.ForeignKey(User,
null=False,
on_delete=models.CASCADE)
objects = ShoppingCartManager()
def __str__(self):
return f'{self.user.username}\'s shopping cart'
这个类定义了一个外键
user
与
User
模型关联,当用户被删除时,购物车也会被删除。同时,我们将自定义的
ShoppingCartManager
分配给
objects
属性,并实现了
__str__
方法,以便在 Django 管理界面中更好地显示购物车信息。
-
创建
ShoppingCartItemManager类 :
class ShoppingCartItemManager(models.Manager):
def get_items(self, cart):
return self.filter(cart_id=cart.id)
这个类只定义了一个方法
get_items
,用于获取购物车中的所有项目。
-
创建
ShoppingCartItem类 :
class ShoppingCartItem(models.Model):
quantity = models.IntegerField(null=False)
price_per_unit = models.DecimalField(max_digits=9,
decimal_places=2,
default=0)
cart = models.ForeignKey(ShoppingCart,
null=False,
on_delete=models.CASCADE)
game = models.ForeignKey(Game,
null=False,
on_delete=models.CASCADE)
objects = ShoppingCartItemManager()
这个类定义了两个属性
quantity
和
price_per_unit
,分别表示商品数量和单价。同时,定义了两个外键
cart
和
game
,分别与
ShoppingCart
和
Game
模型关联。将自定义的
ShoppingCartItemManager
分配给
objects
属性。
- 生成并应用迁移文件 :在终端中运行以下命令:
python manage.py makemigrations
python manage.py migrate
通过以上步骤,我们完成了从价格列表数据模型到购物车模型的创建,为游戏商店应用添加了完整的功能。接下来,我们可以继续为按钮添加功能,实现将商品添加到购物车的功能。
以下是整个流程的 mermaid 流程图:
graph LR
A[创建价格列表数据模型] --> B[创建游戏列表和详情页面]
B --> C[添加游戏列表视图]
C --> D[创建购物车模型]
A --> E[生成并应用迁移文件]
B --> E
C --> E
D --> E
E --> F[启动网站并创建游戏]
F --> G[验证功能]
通过这个流程图,可以清晰地看到整个开发过程的步骤和顺序。
构建游戏商店应用:从价格模型到购物车的实现
5. 为购物车添加功能
现在购物车模型已经创建好,接下来要为“添加到购物车”按钮添加功能,让用户能够将游戏添加到购物车中。
-
创建添加到购物车的视图函数
:编辑
gamestore/main/目录下的views.py文件,添加以下函数:
from django.shortcuts import redirect, get_object_or_404
from .models import Game, ShoppingCart, ShoppingCartItem
def add_to_cart(request, game_id):
game = get_object_or_404(Game, id=game_id)
user = request.user
if user.is_authenticated:
try:
cart = ShoppingCart.objects.get_by_user(user)
except ShoppingCart.DoesNotExist:
cart = ShoppingCart.objects.create_cart(user)
try:
cart_item = ShoppingCartItem.objects.get(cart=cart, game=game)
cart_item.quantity += 1
cart_item.save()
except ShoppingCartItem.DoesNotExist:
cart_item = ShoppingCartItem.objects.create(
cart=cart,
game=game,
quantity=1,
price_per_unit=game.pricelist.price_per_unit
)
return redirect('index')
在这个函数中,首先获取要添加到购物车的游戏对象,然后检查用户是否已登录。如果用户已登录,尝试获取用户的购物车,如果购物车不存在则创建一个新的购物车。接着检查购物车中是否已经存在该游戏的项目,如果存在则增加数量,不存在则创建一个新的购物车项目。最后重定向到首页。
-
更新 URL 配置
:在主应用的
url.py文件中,添加以下 URL 到urlpatterns列表中:
path('cart/add/<int:game_id>/', views.add_to_cart, name='add_to_cart'),
6. 显示购物车内容
接下来,我们要创建一个页面来显示购物车中的内容。
-
创建购物车视图函数
:在
gamestore/main/目录下的views.py文件中添加以下函数:
def view_cart(request):
user = request.user
if user.is_authenticated:
try:
cart = ShoppingCart.objects.get_by_user(user)
cart_items = ShoppingCartItem.objects.get_items(cart)
total_price = sum(item.quantity * item.price_per_unit for item in cart_items)
except ShoppingCart.DoesNotExist:
cart_items = []
total_price = 0
else:
cart_items = []
total_price = 0
context = {
'cart_items': cart_items,
'total_price': total_price
}
return render(request, 'main/cart.html', context)
在这个函数中,首先检查用户是否已登录。如果用户已登录,获取用户的购物车和购物车中的项目,并计算购物车的总价。如果用户未登录,则购物车项目和总价都设为 0。最后将购物车项目和总价传递给模板进行渲染。
-
创建购物车模板
:在
gamestore/main/templates/main/目录下创建cart.html文件,内容如下:
{% extends 'base.html' %}
{% block 'content' %}
<h2>Shopping Cart</h2>
<hr/>
{% if cart_items %}
<table class="table">
<thead>
<tr>
<th>Game</th>
<th>Quantity</th>
<th>Price per Unit</th>
<th>Total Price</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{% for item in cart_items %}
<tr>
<td>{{ item.game.name }}</td>
<td>{{ item.quantity }}</td>
<td>${{ item.price_per_unit|floatformat:2|intcomma }}</td>
<td>${{ item.quantity * item.price_per_unit|floatformat:2|intcomma }}</td>
<td>
<a href="{% url 'remove_from_cart' item.id %}" class="btn btn-danger">Remove</a>
</td>
</tr>
{% endfor %}
<tr>
<td colspan="3"><strong>Total:</strong></td>
<td><strong>${{ total_price|floatformat:2|intcomma }}</strong></td>
<td></td>
</tr>
</tbody>
</table>
{% else %}
<p>Your cart is empty.</p>
{% endif %}
{% endblock %}
在这个模板中,使用表格显示购物车中的项目,包括游戏名称、数量、单价、总价和移除按钮。如果购物车为空,则显示提示信息。
-
更新 URL 配置
:在主应用的
url.py文件中,添加以下 URL 到urlpatterns列表中:
path('cart/view/', views.view_cart, name='view_cart'),
7. 实现移除购物车项目功能
为了让用户能够移除购物车中的项目,我们需要实现移除功能。
-
创建移除购物车项目的视图函数
:在
gamestore/main/目录下的views.py文件中添加以下函数:
def remove_from_cart(request, cart_item_id):
cart_item = get_object_or_404(ShoppingCartItem, id=cart_item_id)
cart_item.delete()
return redirect('view_cart')
在这个函数中,首先获取要移除的购物车项目对象,然后将其从数据库中删除,最后重定向到购物车页面。
-
更新 URL 配置
:在主应用的
url.py文件中,添加以下 URL 到urlpatterns列表中:
path('cart/remove/<int:cart_item_id>/', views.remove_from_cart, name='remove_from_cart'),
8. 优化用户体验
为了提升用户体验,我们可以做一些优化。
-
添加导航栏链接
:在
base.html模板中添加购物车和添加到购物车的链接,让用户可以方便地访问购物车和添加游戏到购物车。
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="{% url 'index' %}">Game Store</a>
</div>
<ul class="nav navbar-nav">
{% if user.is_authenticated %}
<li><a href="{% url 'view_cart' %}">View Cart</a></li>
{% endif %}
</ul>
</div>
</nav>
- 错误处理和提示信息 :在视图函数中添加更多的错误处理和提示信息,让用户在操作出现问题时能够得到明确的反馈。例如,在添加到购物车时,如果游戏价格不可用,可以显示提示信息。
以下是整个购物车功能实现的步骤表格:
| 步骤 | 操作 | 代码文件 | 代码内容 |
| ---- | ---- | ---- | ---- |
| 1 | 创建添加到购物车视图函数 |
views.py
|
add_to_cart
函数 |
| 2 | 更新 URL 配置 |
url.py
|
path('cart/add/<int:game_id>/', views.add_to_cart, name='add_to_cart')
|
| 3 | 创建购物车视图函数 |
views.py
|
view_cart
函数 |
| 4 | 创建购物车模板 |
cart.html
| 购物车模板代码 |
| 5 | 更新 URL 配置 |
url.py
|
path('cart/view/', views.view_cart, name='view_cart')
|
| 6 | 创建移除购物车项目视图函数 |
views.py
|
remove_from_cart
函数 |
| 7 | 更新 URL 配置 |
url.py
|
path('cart/remove/<int:cart_item_id>/', views.remove_from_cart, name='remove_from_cart')
|
| 8 | 优化用户体验 |
base.html
| 添加导航栏链接 |
以下是购物车功能实现的 mermaid 流程图:
graph LR
A[用户点击添加到购物车按钮] --> B{用户是否登录}
B -- 是 --> C{购物车是否存在}
C -- 是 --> D{购物车中是否已有该游戏}
D -- 是 --> E[增加数量]
D -- 否 --> F[创建新购物车项目]
C -- 否 --> F
B -- 否 --> G[提示用户登录]
H[用户点击查看购物车] --> I{用户是否登录}
I -- 是 --> J[显示购物车内容]
I -- 否 --> K[提示用户登录]
L[用户点击移除购物车项目] --> M[移除项目]
M --> J
通过以上步骤,我们完成了游戏商店应用从价格模型到购物车功能的实现,并且对用户体验进行了优化。现在用户可以方便地浏览游戏、将游戏添加到购物车、查看购物车内容和移除购物车项目。
超级会员免费看
1035

被折叠的 条评论
为什么被折叠?



