天天生鲜 - App设计
一、项目目录
daily_fresh_demo - daily_fresh_demo - df_cart # 购物车功能 - df_goods # 商品功能 - df_order # 订单功能 - df_user # 用户功能(包括登录验证等相关功能) - static # 静态文件 - templates # 前段模板- whoosh_index # 全局索引文件 db.sqlite3 manage.py
注:这个电商网站是博主在接触Django之后做的第一个项目,还有很多地方需要改进。如果有需要项目相关视频资源的朋友可以博客园私信,或者评论区留言,博主会在看到的第一时间分享。
附github源码地址(包含静态文件):https://github.com/weilanhanf/daily_fresh_demo
daily_fresh_demo
- daily_fresh_demo
- settings.py
- urls.py
- wsgi.py
- __init__.py # 这里使用Django自带的小型sqlite数据库,如果使用mysql就需要在这里添加相应的驱动
1、全局配置文件settings.py
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'uey!i4x26n!$d-73cs%blri)09#xfud_e361ne2h(#s27)l!'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'df_user',
'df_goods',
'df_cart',
'df_order',
'tinymce',#使用富文本编辑框要在settings文件中安装
'haystack',#全文检索
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'daily_fresh_demo.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')]
,
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'daily_fresh_demo.wsgi.application'
# Database
# https://docs.djangoproject.com/en/2.0/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
},
'OPTIONS':{
'TIMEOUT': 20,
}
}
# Password validation
# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/2.0/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.0/howto/static-files/
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static'),
]
#开发阶段上传文件目录
MEDIA_ROOT = os.path.join(BASE_DIR, 'static')
#部署后的上传文件目录
# MEDIA_ROOT = 'var/www/daily_fresh_demo/static'
#富文本编辑框的使用配置
TINYMCE_DEFAULT_CONFIG = {
'theme': 'advanced',
'width': 600,
'height': 400,
}
HAYSTACK_CONNECTIONS = {
'default':{
#使用whoosh引擎
'ENGINE':'haystack.backends.whoosh_cn_backend.WhooshEngine',
#添加索引文件路径
'PATH':os.path.join(BASE_DIR, 'whoosh_index'),
}
}
#当修改删除数据时,自动生成索引
HAYSTACK_SIGNAL_PROCESSOR ='haystack.signals.RealtimeSignalProcessor'
# HAYSTACK_DEFAULT_OPERATOR = 'OR'
HAYSTACK_SEARCH_RESULTS_PER_PAGE = 18#每一页显示多少数据
2、路由分发urls.py
from django.contrib import admin
from django.urls import path
from django.conf.urls import url, include
urlpatterns = [
path('admin/', admin.site.urls),
url(r'^', include('df_goods.urls',namespace='goods')),
url(r'^user/', include('df_user.urls', namespace='user')),
url(r'^goods/', include('df_goods.urls')),
url(r'^cart/',include('df_cart.urls', namespace='cart')),
url(r'^order/',include('df_order.urls', namespace='order')),
url(r'^search/', include('haystack.urls')),#全文检索
url(r'^tinymce/', include('tinymce.urls')),#使用富文本编辑框配置confurl
]
二、用户相关功能模块
app目录如下
df_user
- migrations # 迁移文件目录
- admin.py
- apps.py
- models.py
- test.py
- urls.py
- user_docorator.py # 这里使用装饰器做用户身份认证
- views.py
- __init__.py
1、apps.py
from django.apps import AppConfig
class DfUserConfig(AppConfig):
name = 'df_user'
2、models.py
from django.db import models
# Create your models here.
class UserInfo(models.Model):
uname=models.CharField(max_length=20)
upwd=models.CharField(max_length=40)
uemail=models.CharField(max_length=30)
ushou=models.CharField(max_length=20,default="")
uaddress=models.CharField(max_length=100,default="")
uyoubian=models.CharField(max_length=6,default="")
uphone=models.CharField(max_length=11,default="")
# default,blank是python层面的约束,不影响数据库表结构,修改时不需要迁移 python manage.py makemigrations
class GoodsBrowser(models.Model):
user=models.ForeignKey('UserInfo', on_delete=models.CASCADE)
good=models.ForeignKey('df_goods.GoodsInfo', on_delete=models.CASCADE)
3、urls.py
#!/user/bin/env python
# -*- coding: utf-8 -*-
from django.conf.urls import url
from . import views
app_name = 'df_user'
urlpatterns = [
url(r'^register/$', views.register),
url(r'^register_handle/$', views.register_handle),
url(r'^register_exist/$', views.register_exist),
url(r'^login/$', views.login),
url(r'^login_handle/$', views.login_handle),
url(r'^info/$', views.info),
url(r'^order/(\d+)$', views.order),
url(r'^site/$', views.site),
# url(r'^place_order/$', views.place_order),
url(r'^logout/$', views.logout)
]
4、view.py
from django.shortcuts import render, redirect, HttpResponseRedirect, HttpResponse
from django.http import JsonResponse
from .models import UserInfo
from df_goods.models import GoodsInfo
from df_user.models import GoodsBrowser
from df_order.models import *
from hashlib import sha1
from . import user_decorator
from django.core.paginator import Paginator,Page
def register(request):
context={
'title':'用户注册',
}
return render(request, 'df_user/register.html', context)
def register_handle(request):
#接受用户输入
post = request.POST
print(request.method)
uname=post.get('user_name')
upwd=post.get('pwd')
upwd2=post.get('cpwd')
uemail=post.get('email')
#判断两次密码一致性
if upwd != upwd2:
return redirect('/user/register/')
#密码加密
s1=sha1()
s1.update(upwd.encode('utf8'))
upwd3=s1.hexdigest()
# sha = hashlib.sha1(upwd.encode('utf8'))
# sha.hexdigest()
#创建对象
user=UserInfo()
user.uname=uname
user.upwd=upwd3
user.uemail=uemail
user.save()
print(uname, upwd3,uemail)
#注册成功
context = {
'title': '用户登陆',
'uname': uname,
}
# return redirect('/user/login/')
return render(request, 'df_user/login.html', context)
def register_exist(request):
uname=request.GET.get('uname')
count=UserInfo.objects.filter(uname=uname).count()
if count == 0:
print('当前用户名可用')
return JsonResponse({'count':count})
# @user_decorator.request_detail
def login(request):
print(request.get_full_path(), 'request.get_full_path')
uname=request.COOKIES.get('uname', '')
context={
'title': '用户登陆',
'error_name':0,
'error_pwd':0,
'uname':uname,
}
return render(request, 'df_user/login.html', context)
def login_handle(request):#没有利用ajax提交表单
#接受请求信息
post = request.POST
uname = post.get('username')
upwd = post.get('pwd')
jizhu = post.get('jizhu', 0)
#根据用户名查询对象
# print(uname, upwd, jizhu, request.method)
users = UserInfo.objects.filter(uname=uname)#[]
print(uname,len(users), users)
#判断如果未查到则用户名错误,如果查到则判断密码是否正确,正确则转到用户中心
if len(users)==1:
s1 = sha1()
s1.update(upwd.encode('utf8'))
if s1.hexdigest()==users[0].upwd:
print("验证成功")
# request.COOKIES['url'] = '/8/'
url = request.COOKIES.get('url','/')
print(url)
red = HttpResponseRedirect(url)#继承与HttpResponse 在跳转的同时 设置一个cookie值
#是否勾选记住用户名,设置cookie
if jizhu!=0:
red.set_cookie('uname', uname)
# print('设置cookie', request.COOKIES['uname'])
else:
red.set_cookie('uname', '',max_age=-1)#设置过期cookie时间,立刻过期
request.session['user_id'] = users[0].id
request.session['user_name'] = uname
return red
else:
context = {
'title':'用户名登陆',
'error_name': 0,
'error_pwd':1,
'uname':uname,
'upwd':upwd,
}
# print('密码错误')
return render(request, 'df_user/login.html', context)
else:
context = {
'title': '用户名登陆',
'error_name': 1,
'error_pwd': 0,
'uname': uname,
'upwd': upwd,
}
print('不存在当前用户')
return render(request, 'df_user/login.html', context)
def logout(request):
request.session.flush()#清空当前用户所有session
return redirect('/')
@user_decorator.login
def info(request):
username =request.session.get('user_name')
# print(username)
user = UserInfo.objects.filter(uname = username).first()
# user = UserInfo.objects.get(id=request.session['user_id'])
# print(request.session['user_name'])
#列表形式最近浏览
# goods_ids = request.COOKIES.get('goods_ids', '')
# print('cookies', goods_ids)
#在cookie中goods_id以{ 'gooids':'1,5,6,7,8,9'}形式存入
# goods_ids1 = goods_ids.split(',')#拆分为列表
# print('最近浏览商品序号',goods_ids1)
# goods_list1 = GoodsInfo.objects.filter(id__in=goods_ids1)#会破坏浏览商品的先后顺序
# if goods_ids1[0] != '' :
# goods_list = [GoodsInfo.objects.get(id=int(goods_id)) for goods_id in goods_ids1]
# # for goods_id in goods_ids1:
# # goods_list.append(GoodsInfo.objects.get(id=int(goods_id)))#pk与id区别
# # 每次只查询一个商品并放入列表的最后,保证了浏览商品的顺序
# explain = '最近浏览'
# else:
# goods_list = []
# explain = '无最近浏览'
# 最近浏览计入第三张那个表
goods_ids = GoodsBrowser.objects.filter(user_id=request.session['user_id'])
# print(goods_ids)
goods_ids1 = [good_browser.good_id for good_browser in goods_ids]
# print(goods_ids1)
# goods_ids2 = []
# for good_id in goods_ids1:
# if good_id not in goods_ids2:
# goods_ids2.append(good_id)
# print(goods_ids2)
if len(goods_ids1) != 0:
goods_list = [GoodsInfo.objects.get(id=goods_id) for goods_id in goods_ids1]
goods_list.reverse()
# print(goods_list)
explain = '最近浏览'
else:
goods_list = []
explain = '无最近浏览'
context={
'title':'用户中心',
'page_name': 1,
'user_phone':user.uphone,
'user_address':user.uaddress,
'user_name':request.session['user_name'],
'goods_list': goods_list,
'explain': explain,
}
# print(user.uname, user.uaddress, user.uphone)
return render(request, 'df_user/user_center_info.html', context)
@user_decorator.login
def order(request, index):
user_id = request.session['user_id']
orders_list = OrderInfo.objects.filter(user_id=int(user_id)).order_by('-odate')
# print(len(orders_list))
# print(orders_list)
paginator = Paginator(orders_list,2)
page = paginator.page(int(index))
context={
'paginator': paginator,
'page':page,
# 'orders_list':orders_list,
'title':"用户中心",
'page_name':1,
}
return render(request, 'df_user/user_center_order.html', context)
@user_decorator.login
def site(request):
user = UserInfo.objects.get(id=request.session['user_id'])
# print(user, type(user), user.uphone,user.uaddress)
if request.method=="POST":
post = request.POST
user.ushou = post.get('ushou')
user.uaddress = post.get('uaddress')
user.uyoubian = post.get('uyoubian')
user.uphone = post.get('uphone')
user.save()
context = {
'page_name': 1,
'title': '用户中心',
'user':user,
}
return render(request, 'df_user/user_center_site.html', context)
5、user_decorator.py
对用户进行身份认证,如用户进入个人中心的时候需要验证,购买商品也需要身份验证
#!/user/bin/env python
# -*- coding: utf-8 -*-
from django.http import HttpResponseRedirect
import re
#如果未登录则转到登陆页面
def login(func):
def login_fun(request, *args, **kwargs):
if 'user_id' in request.session:
return func(request, *args, **kwargs)
else:
red = HttpResponseRedirect('/user/login/')
red.set_cookie('url', request.get_full_path())
print(request.get_full_path(), 'user_decorator')
#保证用户再登陆验证之后仍点击到希望的页面
return red
return login_fun
"""
http://127.0.0.1:8000/200/?type=10
request.path :表示当前路径,为/200/
request.get_full_path():表示完整路径,为/200/?type=10
"""
三、商品相关功能模块
app目录如下
df_goods
- migrations
- admin.py
- apps.py
- models.py
- search_indexes.py # 搜索商品功能
- tests.py
- urls.py
- views.py
- __init__.py
1、app.py
from django.apps import AppConfig
class DfGoodsConfig(AppConfig):
name = 'df_goods'
2、models.py
from django.db import models
from tinymce.models import HTMLField#使用富文本编辑框要在settings文件中安装
#将一对多的关系维护在GoodsInfo中维护,另外商品信息与分类信息都属于重要信息需要使用逻辑删除
# Create your models here.
class TypeInfo(models.Model):#商品分类信息 水果 海鲜等
isDelete = models.BooleanField(default=False)#逻辑删除
ttitle = models.CharField(max_length=20)
def __str__(self):#这里定义在admin中要显示的内容
# return self.ttitle.encode('utf-8')
return self.ttitle
class GoodsInfo(models.Model):#具体商品信息
isDelete = models.BooleanField(default=False)#逻辑删除
gtitle = models.CharField(max_length=20)#商品的名称
gpic = models.ImageField(upload_to='df_goods')#关联图片目录
gprice = models.DecimalField(max_digits=5, decimal_places=2)#商品价格小数位为两位,整数位为3位
gunit = models.CharField(max_length=20, default='500g')#商品单位kg或者个数
gclick = models.IntegerField()#商品点击量
gjianjie = models.CharField(max_length=200)#商品简介
gkucun = models.IntegerField()#商品库存
gcontent = HTMLField()#商品介绍
gtype = models.ForeignKey(TypeInfo, on_delete=models.CASCADE)#外键关联TypeInfo表
# gadv = models.BooleanField(default=False)#商品是否推荐
def __str__(self):
# return self.gtitle.encode('utf-8')
return self.gtitle
# python3中 __str__ 不能接收bytes类型的数据,这和python2/3的编解码方式是有关系的。
3、urls.py
#!/user/bin/env python
# -*- coding: utf-8 -*-
from django.conf.urls import url
from . import views
from .views import *
app_name = 'df_goods'
urlpatterns = [
url('^$', views.index),
url('^list(\d+)_(\d+)_(\d+)/$', views.list),
url('^(\d+)/$', views.detail),
url(r'^search/', MySearchView()),#全文检索
url(r'^cookieTest/', views.cookieTest)
]
4、view.py
from django.shortcuts import render, HttpResponse
from .models import *
from django.core.paginator import Page, Paginator
from df_cart.models import CartInfo
from df_user.models import GoodsBrowser
from haystack.views import SearchView
# Create your views here.
def index(request):
#查询各个分类的最新4条,最热4条数据
typelist = TypeInfo.objects.all()
print(len(typelist), 'asdf')
# 连表操作(了不起的双下划线)利用双下划线和 _set将表之间的操作连接起来
type0 = typelist[0].goodsinfo_set.order_by('-id')[0:4]#按照最新上传的水果显示
type01 = typelist[0].goodsinfo_set.order_by('-gclick')[0:4]#按照用户点击量上传
type1 = typelist[1].goodsinfo_set.order_by('-id')[0:4]
type11 = typelist[1].goodsinfo_set.order_by('-gclick')[0:4]
type2 = typelist[2].goodsinfo_set.order_by('-id')[0:4]
type21 = typelist[2].goodsinfo_set.order_by('-gclick')[0:4]
type3 = typelist[3].goodsinfo_set.order_by('-id')[0:4]
type31 = typelist[3].goodsinfo_set.order_by('-gclick')[0:4]
type4 = typelist[4].goodsinfo_set.order_by('-id')[0:4]
type41 = typelist[4].goodsinfo_set.order_by('-gclick')[0:4]
type5 = typelist[5].goodsinfo_set.order_by('-id')[0:4]
type51 = typelist[5].goodsinfo_set.order_by('-gclick')[0:4]
#判断是否存在登录状态
try:
user_id = request.session['user_id']
cart_count = CartInfo.objects.filter(user_id=int(user_id)).count
except:
cart_count = 0
context = {
'title': '首页',
'cart_count': cart_count,
'guest_cart':1,
'type0':type0, 'type01':type01,
'type1':type1, 'type11':type11,
'type2':type2, 'type21':type21,
'type3':type3, 'type31':type31,
'type4':type4, 'type41':type41,
'type5':type5, 'type51':type51,
}
"""
context = {
'guest_cart':1,
'title': '首页'
}
#获取最新的4个商品
hot = GoodsInfo.objects.all().order_by('-gclick')[0:4]
context.setdefault('hot', hot)
#*******获取各分类下的点击商品*******
#首先获取分类
typelist = TypeInfo.objects.all()
for i in range(len(typelist)):
#获取type对象
type = typelist[i]
#根据type对象获取商品列表
#通过外键关联获取商品
#获取对应列表中的通过id倒序排列的前四个
goods1 = type.goodinfo_set.order_by('-id')[0:4]
goods2 = type.goodinfo_set.order_by('-gclick')[0:4]
key1 = 'type' + str(i) # 根据id 倒叙排列
key2 = 'type' + str(i) + str(i) # 根据点击量倒序排列
context.setdefault(key1, goods1)
context.setdefault(key2, goods2)
print(context)
"""
# print(type0, type01)
# for i in type0:
# print(i.gpic)
return render(request, 'df_goods/index.html', context)
def list(request, tid, pindex, sort):
#tid:商品种类信息 pindex:商品页码 sort:商品显示分类方式
typeinfo = TypeInfo.objects.get(pk=int(tid))
#根据主键查找当前的商品分类 海鲜或者水果
news = typeinfo.goodsinfo_set.order_by('-id')[0:2]
#list.html左侧最新商品推荐
goods_list = []
# list中间栏商品显示方式
':#默认最新
goods_list = GoodsInfo.objects.filter(gtype_id=int(tid)).order_by('-id')
':#按照价格
goods_list = GoodsInfo.objects.filter(gtype_id=int(tid)).order_by('-gprice')
':#按照人气点击量
goods_list = GoodsInfo.objects.filter(gtype_id=int(tid)).order_by('-gclick')
#创建Paginator一个分页对象
paginator = Paginator(goods_list, 4)
#返回Page对象,包含商品信息
page = paginator.page(int(pindex))
context = {
'title': '商品列表',
'guest_cart': 1,
'page': page,
'paginator': paginator,
'typeinfo': typeinfo,
'sort': sort, # 排序方式
'news': news,
}
return render(request,'df_goods/list.html',context)
def detail(request, id):
goods = GoodsInfo.objects.get(pk=int(id))
goods.gclick = goods.gclick+1#商品点击量
goods.save()
news = goods.gtype.goodsinfo_set.order_by('-id')[0:2]
context = {
'title': goods.gtype.ttitle,
'guest_cart': 1,
'goods': goods,
'news': news,
'id':id,
}
response=render(request, 'df_goods/detail.html', context)
#使用列表 记录最近浏览, 在用户中心使用
# goods_ids = request.COOKIES.get('goods_ids', '')#在cookie中建立一个商品id的对应最近浏览的商品
# goods_id = '%d' %goods.id#将url转化为整型
# if goods_ids != '':#判断是否存在浏览记录,如果存在则继续判断,
# goods_ids1 = goods_ids.split(',')#拆分为列表
# if goods_ids1.count(goods_id)>=1:#如果商品已经存在记录则删除旧纪录
# goods_ids1.remove(goods_id)
# goods_ids1.insert(0, goods_id)#将商品插入到第一页
# if len(goods_ids1)>=6:#每页只显示五个最近浏览的商品
# del goods_ids1[5]
# goods_ids = ','.join(goods_ids1)#将商品id拼接为字符串
# else:
# goods_ids = goods_id#显然第一次查看detail页面时为空,则直接添加
# response.set_cookie('goods_ids', goods_ids)#写入cookie
# 将用户最近浏览商品记录进第三张表
'''
1,判断是否有用户登录, 如果没有直接结束
2,判断在当前浏览表中是否存在这个用户,
不存在则创建一个用户浏览记录,并且不用判断是否浏览过
若存在则判断当前用户是否存在一个浏览过当前商品
3,不管有没有浏览过当前商品都要先创建一个商品记录放入表中
如果浏览过则删除前期浏览的商品
若没有则不用删除
4,如果商品记录为五条,则将最后的一条删除
'''
try:
user_id = request.session['user_id']
# user_list = GoodsBrowser.objects.filter(user_id=int(user_id))
goods_browser = GoodsBrowser()
goods_browser.user_id = int(user_id)
goods_browser.good_id = int(id)
goods_browser.save()
old_user_list = GoodsBrowser.objects.filter(user_id=int(user_id), good_id=int(id))
if len(old_user_list) > 1:
GoodsBrowser.objects.filter(good_id=int(id)).first().delete()
if len(GoodsBrowser.objects.filter(user_id=int(user_id))) > 5:
GoodsBrowser.objects.filter(user_id=int(user_id)).first().delete()
except:
pass
return response
def cart_count(request):
if request.session.has_key('user_id'):
return CartInfo.objects.filter(user_id=request.session['user_id']).count
else:
return 0
class MySearchView(SearchView):
def extra_context(self):
context = super(MySearchView, self).extra_context()
context['title'] = '搜索'
context['guest_cart'] = 1
context['cart_count'] = cart_count(self.request)
return context
def cookieTest(request):
response = HttpResponse()
if request.COOKIES.get('binggan'):#判断是否有此cookie
cookie = request.COOKIES
response.write(cookie['binggan'])#如果有则写入
return response
5、admin.py
from django.contrib import admin
from .models import TypeInfo,GoodsInfo
# Register your models here.
#注册模型类 普通方法
class TypeInfoAdmin(admin.ModelAdmin):
list_display = ['id', 'ttitle']
# class GoodsInfoAdmin(admin.ModelAdmin):
# list_per_page = 15
# list_display = ['id', 'gtitle', 'gunit','gclick', 'gprice','gpic','gjianjie','gkucun','gcontent','gjianjie']
admin.site.register(TypeInfo, TypeInfoAdmin)
# admin.site.register(GoodsInfo, GoodsInfoAdmin)
# 装饰器方法
@admin.register(GoodsInfo)
class GoodsInfoAdmin(admin.ModelAdmin):
list_per_page = 15
list_display = ['id', 'gtitle', 'gunit','gclick', 'gprice','gpic','gjianjie','gkucun','gcontent','gjianjie']
6、search_indexes.py
#!/user/bin/env python
# -*- coding: utf-8 -*-
from haystack import indexes
from .models import *
#制定对于某个类的某些数据建立索引
class GoodsInfoIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
def get_model(self):#对GoodsInfo模型类进行索引
return GoodsInfo
def index_queryset(self, using=None):
return self.get_model().objects.all()
全局索引提供一个大的全局搜索功能,也可以通过Django 中的ORM的Q查询实现
这里请参考全局索引:https://www.cnblogs.com/welan/p/9231430.html
四、订单相关功能
app目录如下
df_order
- migrations
- admin.py
- apps.py
- models.py
- tests.py
- urls.py
- views.py
- __init__.py
1、app.py
from django.apps import AppConfig
class DfOrderConfig(AppConfig):
name = 'df_order'
2、models.py
from django.db import models
# Create your models here.
class OrderInfo(models.Model):#大订单
oid = models.CharField(max_length=20, primary_key=True)#订单号
user = models.ForeignKey('df_user.UserInfo', on_delete=models.CASCADE)#确定哪个用户的订单
odate = models.DateTimeField(auto_now=True)
oIsPay = models.BooleanField(default=False)#当前订单是否支付,默认为否
ototal = models.DecimalField(max_digits=8, decimal_places=2)
oaddress = models.CharField(max_length=150)
#虽然订单总价可以由多个商品的单价以及数量求得,但是由于用户订单的总价的大量使用,忽略total的冗余度
#无法实现:真实支付,物流信息
class OrderDetailInfo(models.Model):#大订单中的具体某一商品订单
goods = models.ForeignKey('df_goods.GoodsInfo',on_delete=models.CASCADE)#关联商品信息
order = models.ForeignKey('OrderInfo', on_delete=models.CASCADE)#关联大订单,确定属于某一个大订单中
price = models.DecimalField(max_digits=6, decimal_places=2)#某一类商品订单的价格最高达9999.99
count = models.IntegerField()
3、urls.py
#!/user/bin/env python
# -*- coding: utf-8 -*-
from django.conf.urls import url
from . import views
app_name = 'df_order'
urlpatterns = [
url(r'^$', views.order),
url(r'^push/$', views.order_handle),
]
4、view.py
from django.shortcuts import render,HttpResponse,redirect
from df_user import user_decorator
from django.db import transaction
from django.http import JsonResponse
from datetime import datetime
from decimal import Decimal
from .models import *
from df_cart.models import CartInfo
from df_user.models import UserInfo
# Create your views here.
@user_decorator.login
def order(request):
uid = request.session['user_id']
user = UserInfo.objects.get(id=uid)
cart_ids = request.GET.getlist('cart_id')
carts = []
total_price = 0
for goods_id in cart_ids:
cart = CartInfo.objects.get(id = goods_id)
carts.append(cart)
total_price = total_price + float(cart.count) * float(cart.goods.gprice)
total_price = float('%0.2f'%total_price)
trans_cost = 10#运费
total_trans_price = trans_cost + total_price
# print(total_trans_price)
# import datetime订单提交时间
# value = datetime.datetime.now()
# print(value)
context = {
'title': '提交订单',
'page_name': 1,
'user':user,
'carts': carts,
'total_price':float('%0.2f'%total_price),
'trans_cost': trans_cost,
'total_trans_price': total_trans_price,
# 'value':value
}
return render(request, 'df_order/place_order.html', context)
'''
事务提交:
这些步骤中,任何一环节一旦出错则全部退回1
1. 创建订单对象
2. 判断商品库存是否充足
3. 创建 订单 详情 ,多个
4,修改商品库存
5. 删除购物车
'''
@user_decorator.login
@transaction.atomic()#事务
def order_handle(request):
tran_id = transaction.savepoint()#保存事务发生点
cart_ids = request.POST.get('cart_ids')#用户提交的订单购物车,此时cart_ids为字符串,例如'1,2,3,'
# print('订单购物车', cart_ids)
user_id = request.session['user_id']#获取当前用户的id
# print('当前用户', user_id)
try:
order_info = OrderInfo()#创建一个订单对象
now = datetime.now()
order_info.oid = '%s%d'%(now.strftime('%Y%m%d%H%M%S'), user_id)#订单号为订单提交时间和用户id的拼接
order_info.odate = now#订单时间
# print('订单时间', now)
order_info.user_id = int(user_id)#订单的用户id
order_info.ototal = Decimal(request.POST.get('total'))#从前端获取的订单总价
# print('总价', order_info.ototal)
order_info.save()#保存订单
for cart_id in cart_ids.split(','):#逐个对用户提交订单中的每类商品即每一个小购物车
cart = CartInfo.objects.get(pk = cart_id)#从CartInfo表中获取小购物车对象
order_detail = OrderDetailInfo()#大订单中的每一个小商品订单
order_detail.order = order_info#外键关联,小订单与大订单绑定
goods = cart.goods#具体商品
if cart.count <= goods.gkucun:#判断库存是否满足订单,如果满足,修改数据库
goods.gkucun = goods.gkucun - cart.count
goods.save()
# print(goods.gtitle,'库存', goods.gkucun)
order_detail.goods = goods
order_detail.price = goods.gprice
# print('小订单价格',order_detail.price)
order_detail.count = cart.count
# print('小订单商品数量', order_detail.count)
order_detail.save()
cart.delete()#并删除当前购物车
else:#否则,则事务回滚,订单取消
transaction.savepoint_rollback(tran_id)
return HttpResponse('库存不足')
# return redirect('/cart/')
data = {
'ok': 1,
}
transaction.savepoint_commit(tran_id)
except Exception as e:
print("%s"%e)
print('未完成订单提交')
transaction.savepoint_rollback(tran_id)#事务任何一个环节出错,则事务全部取消
# return HttpResponse('订单提交成功')
return JsonResponse(data)
@user_decorator.login
def pay(request):
pass
五、购物车相关模块功能
app目录如下
df_cart
- migrations
- admin.py
- apps.py
- models.py
- test.py
- urls.py
- views.py
- __init__.py
1、app.py
from django.apps import AppConfig
class DfCartConfig(AppConfig):
name = 'df_cart'
2、models.py
from django.db import models
# Create your models here.
#当一对多关系时例如生鲜分类对生鲜具体商品, 将关系维护在多的那张表中,即在具体商品表中维护
#当多对多关系,则新建一张表,在再第三张表中维护表关系
#用户表与商品表则将关系维护在购物车表中
#在购物车的逻辑删除与物理删除 选择物理删除,
#购物车中的商品不属于重要的信息,可以直接删除
class CartInfo(models.Model):
user = models.ForeignKey('df_user.UserInfo', on_delete=models.CASCADE)
goods = models.ForeignKey('df_goods.GoodsInfo', on_delete=models.CASCADE)
#在同级工程目录下引用外键
count = models.IntegerField()#记录用户买个多少单位的商品
3、urls.py
#!/user/bin/env python
# -*- coding: utf-8 -*-
from django.conf.urls import url
from . import views
app_name = 'df_cart'
urlpatterns = [
url(r'^$', views.cart),
url(r'^add(\d+)_(\d+)/$', views.add),
url(r'^edit(\d+)_(\d+)/$', views.edit),
url(r'^delete(\d+)/$', views.delete),
]
4、view.py
from django.shortcuts import render,redirect
from django.http import JsonResponse
from df_user import user_decorator
from .models import *
# Create your views here.
@user_decorator.login
def cart(request):
uid = request.session['user_id']
carts = CartInfo.objects.filter(user_id=uid)
context = {
'title':'购物车',
'page_name':1,
'carts':carts
}
if request.is_ajax():
count = CartInfo.objects.filter(user_id=request.session['user_id']).count()
#求当前用户购买了几件商品
return JsonResponse({'count': count})
else:
return render(request, 'df_cart/cart.html', context)
@user_decorator.login
def add(request, gid, count):
uid = request.session['user_id']
gid = int(gid)
count = int(count)
print(gid, count)
#查询购物车中是否已经有此商品,如果有则数量增加,如果没有则新增
carts = CartInfo.objects.filter(user_id=uid, goods_id=gid)
if len(carts)>=1:
cart = carts[0]
cart.count = cart.count + count
else:
cart = CartInfo()
cart.user_id = uid
cart.goods_id = gid
cart.count = count
cart.save()
#如果是ajax提交则直接返回json,否则转向购物车
if request.is_ajax():
count = CartInfo.objects.filter(user_id=request.session['user_id']).count()
#求当前用户购买了几件商品
return JsonResponse({'count': count})
else:
return redirect('/cart/')
@user_decorator.login
def edit(request, cart_id, count):
try:
cart = CartInfo.objects.get(pk=int(cart_id))
cart.count=int(count)
cart.save()
data = {'count':0}
except Exception as e:
data = {'count':count}
return JsonResponse(data)
@user_decorator.login
def delete(request,cart_id):
print(cart_id)
try:
cart = CartInfo.objects.get(pk=int(cart_id))
cart.delete()
data={'ok':1}
print('数据库修改成功')
except Exception as e:
data = {'ok':0}
return JsonResponse(data)
天天生鲜 - App设计的更多相关文章
- Livecoding.tv 现正举行iOS及Android App设计比赛
近日,Livecoding.tv, 一个为世界各地的程序员提供在线实时交流的平台,在其网站上发布了一篇通知, 宣布从4月15日至5月15日,会为iOS和Android的开发者举办一场本地移动app设计 ...
- 安卓app设计规范整理和Android APP设计篇(转)
随着安卓智能手机不停的更新换代.安卓手机系统越来越完美,屏幕尺寸也越来越大啦!比如最近小米的miui 6的发布和魅族手机系统的更新等等. 以小米MIUI6的安卓手机来说,MIUI6进行了全新设计,坚持 ...
- 最实用的APP界面设计知识,有温度的APP设计(转)
在逛简书的时候,无意之间看到了这样的一篇非常有意思的app设计博文.顾25学堂的摘录了其中的一些关于移动端APP界面设计的精华.分享给25学堂的app设计师们. 当然,下面的这些app设计知识点是来自 ...
- APP设计师拿到APP产品原型开始,七步搞定APP设计(转)
任何一款成功的APP都需要以坚实的产品概念作为基础,因为概念决定了产品最终完成的潜力. 一般情况下,交到app设计师手里的都是移动app产品原型图.当然这个是在移动产品经理反复斟酌,并且与大家开会讨论 ...
- 学习笔记:只有一套app设计稿(5s尺寸)切出4和4s尺寸以及安卓系统主流尺寸的图
如何在只有一套app设计稿(5s尺寸)切出4和4s尺寸以及安卓系统主流尺寸的图 转自:http://www.zhihu.com/question/23255417 版权归原作者所有 目前ios手机 ...
- 2014年的Google I/O app设计中的材料设计-渣渣的翻译
又是一篇翻译,用了三个多小时.http://android-developers.blogspot.co.id/2014/08/material-design-in-2014-google-io-ap ...
- 必胜宅急送Web app设计背后的思考
O2O模式是餐饮业在移动消费趋势下主动拥抱互联网的方向,迎合餐饮消费者从以往经验判断为主转变为依靠移动设备.lbs.社交网络进行立体决策的过程.继App客户端之后,手机web app也逐渐成为O2O中 ...
- “乐”动人心--2017年10款最佳音乐类APP设计盘点
在上下班的路上,听几首自己喜欢的音乐来打发无聊的等公交车和地铁的时间是现代年轻人的常态.音乐作为最能鼓动人心的"语言",也成为了人们在互联网生活里占比例最高的消费活动之一,一款好看 ...
- Django之天天生鲜项目
准备工作 1.配置settings.py内置文件 注意: AUTH_USER_MODEL配置参数要在第一次迁移数据库之前配置,否则可能django的认证系统工作不正常 2.创建应用 3.配置主路由 一 ...
随机推荐
- Codeforces Round #425 (Div. 2) B. Petya and Exam(字符串模拟 水)
题目链接:http://codeforces.com/contest/832/problem/B B. Petya and Exam time limit per test 2 seconds mem ...
- C#6.0语言规范(十) 类
类是可以包含数据成员(常量和字段),函数成员(方法,属性,事件,索引器,运算符,实例构造函数,析构函数和静态构造函数)和嵌套类型的数据结构.类类型支持继承,这是一种派生类可以扩展和专门化基类的机制. ...
- odoo开发笔记 -- odoo仪表板集成hightcharts
highcharts图表插件初探 http://www.cnblogs.com/liubei/p/highchartsOption.html
- odoo开发环境搭建(二):安装Ubuntu 17虚拟机
odoo开发环境搭建(二):安装Ubuntu 17虚拟机 下载镜像文件: 配置网络: 安装vmware tools: 配置共享文件夹: 选中虚拟机,右键编辑设置里边配置共享文件夹,指定windows本 ...
- (转)WebSphere禁用SSLv3和RC4算法教程
原文:https://www.cnblogs.com/lsdb/p/7126399.html WebSphere经常会报“SSL 3.0 POODLE攻击信息泄露”和"SSL/TLS 受诫礼 ...
- 【VC版】如何获取其他进程中ListView控件中的内容
如果需要C#版的,可以看下我之前写的:C#如何获取其他程序ListView控件中的内容 获取其他进程的数据需要使用到以下几个函数: VirtualAllocEx() VirtualFreeEx() W ...
- Java8简明指南
Java8简明指南 转载自并发编程网 – ifeve.com本文链接地址: Java8简明指南 欢迎来到Java8简明指南.本教程将一步一步指导你通过所有新语言特性.由短而简单的代码示例,带你了解如何 ...
- 修改gitlab仓库地址
最近使用GitLab 搭建了Git的私有仓库,但是发现私有仓库的地址是一串序列号,搞了半天克隆时都是提醒仓库无效,后来才觉得不对,不是本机的IP地址如图 对此我们需要修改gitlab.yml文件: 1 ...
- Spring Boot打包war jar 部署tomcat
概述 1.Spring Boot聚合工程打包war部署Tomcat 2.Spring Boot打包Jar,通过Java -jar直接运行. 3.提供完整pom.xml测试项目 至github 4.项目 ...
- 基于HA机制的MyCat架构——配置HAProxy
HAProxy简介HAProxy提供高可用性.负载均衡以及基于TCP和HTTP应用的代理,支持虚拟主机,它是免费.快速并且可靠的一种解决方案. HAProxy特别适用于那些负载特大的web站点,这些站 ...