django项目总结
1、setting文件配置
1.1 基本配置
INSTALLED_APPS = [
'df_goods', ## 创建应用时,应在此处注册应用,否则不能进行迁移
'user',
'haystack',
'df_order',
]
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
# 在项目中创建templates文件夹,用来存放模板html
'DIRS': [os.path.join(BASE_DIR, 'templates')],
.......
]
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'freshdata',
'HOST': 'localhost',
'USER': 'root',
'PASSWORD': '123456',
'POST': 3306
}
}
LANGUAGE_CODE = 'zh-hans' # 中文
TIME_ZONE = 'Asia/Shanghai' # 上海时间
# 在项目目录下创建static静态文件夹,存放照片,js,css文件
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static')
]
1.3 用户上传图片
# 网页上传照片
MEDIA_ROOT = os.path.join(BASE_DIR,'static')
模型models.py里
上传的照片将会保存在/static/film_img/目录下
class ImageInfo(models.Model):
img = models.ImageField(upload_to='film_img', null=True, blank=True)
imglink = models.ForeignKey('BlogInfo')
def __str__(self):
return self.imglink.title
1. 需要添加enctype="multipart/form-data"
2. 文件上传<input type="file" name="pic"><br>
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<label for="">title</label><br>
<input type="text" name="film_name"><br>
<label for="">content</label><br>
<textarea name="content" id="content1" cols="30" rows="10"></textarea><br>
<input type="file" name="pic"><br>
<input type="submit" value="添加"><br>
</form>
1.4 全文检索插件
全文检索步骤
# 全文检索框架配置
HAYSTACK_CONNECTIONS = {
'default': {
搜索引擎whoosh_cn_backend已经替换掉原来的whoosh_backend
'ENGINE': 'haystack.backends.whoosh_cn_backend.WhooshEngine',
# 索引文件路径
'PATH': os.path.join(BASE_DIR, 'whoosh_index'),
}
}
# 当执行增删改操作时自动生成索引
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
# 搜索结果会自动分页,每页多少个
HAYSTACK_SEARCH_RESULTS_PER_PAGE = 10
- pip install django-haystack==2.8 whoosh jieba
- 将haystack添加到应用里
- 在需要检索的应用中新建search_indexes.py,并复制代码 名字不能错
from haystack import indexes
from df_goods.models import GoodsInfo
class GoodsInfoIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
def get_model(self):
return GoodsInfo
def index_queryset(self, using=None):
return self.get_model().objects.all()
- 在templates目录下新建search/indexes/应用名的文件夹 search/indexes/df_goods
- 设置whoosh
在虚拟环境下的site_packages/haystack/backends目录下找到whoosh_backends.py
复制一份命名为whoosh_cn_backends.py
找到 analyzer=StemmingAnalyzer() 这句代码 替换成analyzer=ChineseAnalyzer()
from jieba.analyse import ChineseAnalyzer 从结巴分词导入中文分词功能
python manage.py rebuild_index 生成 索引文件
若有问题,则向上查看步骤是否出错 - 配置路由
url(r’^search/’, include(‘haystack.urls’)),
其实可以写成
url(r’^$’, SearchView(), name=‘haystack_search’), - 搜索按钮,将搜索代码变成表单,输入框的name属性的值是q
<div class="search_con fl">
<form action="/search/" method="get">
<input type="text" class="input_text fl" name="q" placeholder="搜索商品">
<input type="submit" class="input_btn fr" name="" value="搜索">
</form>
</div>
- 搜索的视图函数默认模板是templates/search/search.html
要将需要搜索的list.html复制一份并命名为search.html
只对page页码对象起作用,要添加object,比如原本的{{ pa.id}} 变成 {{ pa.object.id }}
<ul class="goods_type_list clearfix">
{% for pa in page.object_list %}
<li>
<a href="/detail_{{ pa.object.id }}/"><img src="/static/{{ pa.object.gpic }}"></a>
<h4><a href="/detail_{{ pa.object.id }}/">{% highlight pa.object.gtitle with query %}</a></h4>
<div class="operate">
<span class="prize">¥{{ pa.object.gprice }}</span>
<span class="unit">{{ pa.object.gprice }}/{{ pa.object.gunit }}</span>
<a href="#" class="add_goods" title="加入购物车" name="{{ pa.object.id }}"></a>
</div>
</li>
{% empty %}
<div class="no-post">没有搜索到你想要的结果!</div>
{% endfor %}
</ul>
- 可以在应用中写搜索的视图函数,新建search_views.py,按照写代码
此时路由配置是url(r'^$', SearchView(), name='haystack_search')
from haystack.views import SearchView
from df_goods.models import *
class MySearchView(SearchView):
def extra_context(self):
context = super().extra_context()
覆盖原来的搜索的视图函数,在原来的基础上向search.html页面
传参数new
gnew = GoodsInfo.objects.all().order_by('-id')[:2]
context['new'] = gnew
return context
1.5 调用支付宝接口
支付宝
- 在setting配置文件中添加支付宝id账号以及调用的url
setting配置中
# 支付宝
ALIPAY_APPID = "2016102500758593"
ALIPAY_URL = "https://openapi.alipaydev.com/gateway.do"
-
安装
pip install python-alipay-sdk==1.1
从 1.3.0升级上来的用户, 请先卸载pycrypto: pip uninstall pycrypto
安装python-alipay-sdk
pip install python-alipay-sdk --upgrade -
随意在一个文件夹中打开cmd,生成密钥文件
openssl
OpenSSL> genrsa -out app_private_key.pem 2048 私钥
OpenSSL> rsa -in app_private_key.pem -pubout -out app_public_key.pem # 导出公钥
OpenSSL> exit -
将公钥的内容复制到
支付宝沙盒链接
获取支付宝的公钥 -
将app_public_key.pem复制一份并命名为alipay_public_key.pem,将获得的支付宝公钥复制在此文件中
-
将私钥和支付宝公钥文件复制到static文件中
-
在视图函数中配置
from django.conf import settings
import os
from alipay import AliPay
from django.core.paginator import Paginator
alipay = AliPay(
appid=settings.ALIPAY_APPID,
app_notify_url=None, # 验证地址
app_private_key_path=os.path.join(settings.STATICFILES_DIRS[0],'app_private_key.pem'),
alipay_public_key_path=os.path.join(settings.STATICFILES_DIRS[0],'alipay_public_key.pem'),
sign_type='RSA2', # 加密方式
debug=True,
)
@transaction.atomic
def pay_order(request):
......
.......
elif pay_style == 'zfb':
order_id = order1.oid
order_string = alipay.api_alipay_trade_page_pay(
out_trade_no=order_id,
total_amount=str(total),
subject=f'靓仔坚专属红包{order_id[:5]}.....',
return_url='http://127.0.0.1:8000/check_pay/?order_id='+order_id,
notify_url='http://127.0.0.1:8000/check_pay/?order_id='+order_id
)
# 将拼接的地址传到html中
alipay_url = settings.ALIPAY_URL + '?' + order_string
return JsonResponse({'res':'zfb','alipay_url':alipay_url})
else:
# 有问题就回滚到保存点
transaction.savepoint_rollback(sid)
return JsonResponse({'res': 0})
def check_pay(request):
"""登陆支付页面后,检验支付情况"""
order_id = request.GET.get('order_id')
order1 = OrderInfo.objects.get(oid=order_id)
while True:
time.sleep(1)
# 调用alipay工具查询支付结果
response = alipay.api_alipay_trade_query(order_id) # response是一个字典
# 判断支付结果
print(response)
code = response.get("code") # 支付宝接口调用成功或者错误的标志
trade_status = response.get("trade_status") # 用户支付的情况
if code == "10000" and trade_status == "TRADE_SUCCESS":
# 表示用户支付成功
# 返回前端json,通知支付成功
order1.oIsPay = True
order1.save()
return redirect('/user_center/')
elif code == "40004" or (code == "10000" and trade_status == "WAIT_BUYER_PAY"):
# 表示支付宝接口调用暂时失败,(支付宝的支付订单还未生成) 后者 等待用户支付
# 继续查询
continue
else:
return JsonResponse({'res':0})
1.6 富文本编辑器
- 安装百度编辑器
需要进入虚拟环境安装
解压到项目中,打开Ueditor,在当前目录下打开终端,python setup.py install - 在setting中 注册应用
INSTALLED_APPS = [‘DjangoUeditor’,] - 在项目urls中 配置路由
url(r’^ueditor/’,include(‘DjangoUeditor.urls’)) - 使用UEditorField 替换原来的模型类中的字段
UEditorField(width=1000,height=300,toolbars=‘full’)
比如
content = UEditorField(width=1000,height=300,toolbars='full')
1.7 markdown
安装 markdown:
pip install markdown
def detail(request, num):
# film = BlogInfo.objects.get(id=num)
# 如果没有找到对象就返回404,找到了就正常返回
film = get_object_or_404(BlogInfo, id=num)
film.content = markdown.markdown(film.content, extensions={
'markdown.extensions.extra',
'markdown.extensions.codehilite',
'markdown.extensions.toc'
})
form = CommentForm() # 创建表单对象
return render(request, 'blog/detail.html', {'film': film, 'form': form})
高亮 markdown.extensions.codehilite
-
pip install pygments (高亮)
原理:将代码块的每个单词加个span标签 -
将高亮样式文件放入静态文件
-
在h5页面中 引入高亮样式
<link rel="stylesheet" href="/static/css/highlights/github.css">
2、模型
2.1 模型字段
DecimalField模型类的参数有两个
1、max_digits 整数部分的长度
2、decimal_places
小数部分的长度(保留的小数位数)
比如:666666.66
ototal = models.DecimalField(max_digits=6,decimal_places=2) # 订单总价格
2.2 引用其他应用的模型
例如goods字段,引用了商品应用的GoodsInfo模型类
格式:应用名.模型类
class OrderDetailInfo(models.Model):
goods = models.ForeignKey('df_goods.GoodsInfo')
order = models.ForeignKey('OrderInfo')
price = models.DecimalField(max_digits=5,decimal_places=2)
count = models.IntegerField()
2.3 数据库问题
迁移步骤:
- python manage.py makemigrations
- python manage.py migrate
操作过程中,需要改字段,但修改不了,只能删表重建,并迁移。
问题1:
删表后无法重新建表
解决方法:
删除应用中migrations的initial.py文件以及其他的迁移文件
再重新迁移操作
如果依旧无法迁移,则在数据库中执行
delete from django_migrations where app = “your-app-name”;
清空有外键关联的表中内容
- 清空表中内容
truncate table your_table_name;
有外键时:
暂时关闭外键即可清空,再打开外键
1、SET FOREIGN_KEY_CHECKS=0; 关闭外键
2、SET FOREIGN_KEY_CHECKS=1; 开启外键
3、视图函数
3.1 ajax 和 form表单提交
- ajax提交的方式一般是get
- ajax的通式:(jquery)
视图函数返回的Jsonresponse({'is_ok':1})
$(function(){
$.ajax({
type: 'get',
url: '/cart_ver/',
data: {'good_id': '{{ good.id }}', 'count': anum},
DataType: 'json'
}).done(function (data) {
if (data.is_ok){
.......
}
})
})
$(function(){
$.get(url,data,function(data){
......
})
})
form表单提交的方式有两种,GET和POST
如果POST提交的情况下,表单下应加上csrf验证 {% csrf_token %}
<form action="/login_ver/" method="post">
{% csrf_token %}
<input type="text" name="username" class="name_input" placeholder="请输入用户名" value={{ user_name }}>
<div class="user_error">用户名输入错误或者不存在</div>
<input type="password" name="pwd" class="pass_input" placeholder="请输入密码">
<div class="pwd_error">密码输入错误</div>
<div class="more_input clearfix">
<input type="checkbox" name="remember" id="rem">
<label>记住用户名</label>
<a href="#">忘记密码</a>
</div>
<input type="submit" name="" value="登录" id="btn1" class="input_submit">
</form>
ajax和form的差距:
ajax提交不会刷新页面,form表单提交会刷新页面
-视图函数中获取ajax或form提交的请求,再由视图函数处理请求
只能接受一个请求
3.2 session和cookies
cookies是存放在客户端
session是存放在服务器
session一般保存客户id和密码
# 设置session
request.session['user_id'] = userinfo[0].id
request.session['username'] = username
request.session['pwd'] = pwds
# 获取session
user_id = request.session['user_id']
username = request.session['username']
pwd = request.session['pwd']
# 删除session
request.session.clear()
cookies设置
例如最近浏览商品
def detail(request,num):
good = GoodsInfo.objects.get(id=num)
# 点击后点击量加一
good.gclick += 1
good.save()
# 除了推荐外的食物
other = GoodsInfo.objects.exclude(id=num)
types = TypeInfo.objects.filter(isDelete__isnull=False)
# 最近浏览对象,设置cookies
res = render(request,'detail.html',{'good':good,'other':other,'types':types})
# 先获取cookies的recent,如果没有返回一个列表字符串
recent_goods = request.COOKIES.get('recent','[]')
ren = eval(recent_goods) # 获得列表
# ren = []
# 如果该商品id不在列表中,那ren最前面添加此商品
if num not in ren:
ren.insert(0,num)
if len(ren) > 5:
# 如果超过五个,就把最后一个删除
ren.pop()
res.set_cookie('recent',str(ren))
return res
def user_center(request):
# 如果能获取到session,才能进入个人中心
if request.session.get('username'):
username = request.session.get('username')
user = UserInfo.objects.get(user_name=username) # user为具体对象
# 一开始没有地址,则显示空
addr = user.useradressinfo_set.all().filter(isselect=True) # addr是对象集
# 获取最近浏览的商品
recents = request.COOKIES.get('recent','[]')
ren = eval(recents)
if ren:
# 如果没有浏览商品则返回空列表
ren_goods = [GoodsInfo.objects.get(id=num) for num in ren]
else:
ren_goods = []
if addr:
# 如果有数据
data = addr[0]
else:
# 如果没数据,则addr为空
data = addr
return render(request, 'user_center_info.html', {'ren_goods':ren_goods,'title':'用户中心','user': user, 'username': username, 'addr': data, 'search': 1})
else:
return redirect('/login/')
3.3 数据库的事务
from django.db import transaction # 导入事务模块
在需要事务的函数上添加装饰器
@transaction.atomic
def pay_order(request):
# {'cid':cid,'pay_style':pay_style}
pay_style = request.GET.get('pay_style') # 支付方式
cid = request.GET.getlist('cid') # 要购买商品的id
user_id = request.session['user_id'] # 用户id
user = UserInfo.objects.get(id=user_id) # 用户对象
# 如果没有地址,不可以提交
addr = user.useradressinfo_set.all().get(isselect=True) # 默认地址
sid = transaction.savepoint() # 创建一个保存点,提交订单失败可回退到这一步
if 没问题了就可以提交事务:
transaction.savepoint_commit(sid)
else: 如果有问题就回滚到保存点
transaction.savepoint_rollback(sid)