Django其他tips
目录:
支持跨域
由于js的同源策略,询问后端是否允许进行跨域访问,采用Middle中间件支持
from django.utils.deprecation import MiddlewareMixin
class MyCors(MiddlewareMixin):
def process_response(self, request, response):
response["Access-Control-Allow-Origin"] = "*"
if request.method == "OPTIONS":
response["Access-Control-Allow-Headers"] = "Content-Type"
response["Access-Control-Allow-Methods"] = "GET, POST, PUT, PATCH, DELETE"
return response
然后在setting的MIDDLEWARE中配置即可
或者
安装django-cors-headers
pip install django-cors-headers
配置settings.py
INSTALLED_APPS = [
......
'corsheaders',
......
]
MIDDLEWARE = [
......
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
......
]
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True
Admin支持上传图片
setting中配置
# Media配置
MEDIA_URL = "media/"
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
model的字段配置
course_img = models.ImageField(upload_to="course/%Y-%m", verbose_name='课程的图片')
url
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api/course/', include("course.urls")),
# media路径配置
url(r'media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT})
]
ContentType
有以下两个表,水果,食物
class Fruit(models.Model):
"""
id name
1 西瓜
2 香蕉
"""
name = models.CharField(max_length=32)
class Food(models.Model):
"""
id name
1 旺仔小馒头
2 卫龙辣条
"""
name = models.CharField(max_length=32)
如果设置一个优惠券的表,关联这两个表,如果为每个表都都单独建一列的话就会造成数据表臃肿
class Coupon(models.Model):
"""
id title food_id fruit_id
1 西瓜八折 1 null
2 卫龙买一送一 null 1
"""
title = models.CharField(max_length=32)
food = models.ForeignKey(to="Food")
fruit = models.ForeignKey(to="Fruit")
再用一个表来存一下
class Coupon(models.Model):
title = models.CharField(max_length=32)
table = models.ForeignKey(to="Mytables")
object_id = models.IntegerField()
class MyTables(models.Model):
"""
id table_name
1 Food
2 Fruit
"""
table_name = models.CharField(max_length=32)
table存储表名,object存储在对应表的id
django提供了ContentType
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
class Fruit(models.Model):
name = models.CharField(max_length=32)
coupons = GenericRelation(to="Coupons")
class Food(models.Model):
name = models.CharField(max_length=32)
coupons = GenericRelation(to="Coupons")
class Coupon(models.Model):
title = models.CharField(max_length=32)
content_type = models.ForeignKey(to=ContentType)
object_id = models.IntegerField()
content_object = GenericForeignKey("content_type", "object_id")
使用
from .models import Book, Food, Fruit, Coupon
from django.contrib.contenttypes.models import ContentType
class TestView(APIView):
def get(self, request):
food_obj = Food.objects.filter(id=2).first()
Coupon.objects.create(title="卫龙买一送一", content_object=food_obj)
# 也可以写为
# Coupon.objects.create(title="卫龙买一送一", content_type_id=9, object_id=2)
# 这个content_type_id是在djangocontent
# 对象绑定所有优惠券
print(food_obj.conpons.all())
# 查询优惠券绑定对象
coupon_obj = Coupon.objects.filter(id=1).first()
print(coupon_obj.content_object.name)
也可以
from .models import Book, Food, Fruit, Coupon
from django.contrib.contenttypes.models import ContentType
class TestView(APIView):
def get(self, request):
content_type_obj = ContentType.objects.filter(model="food").first()
model_class = content_type_obj.model_class()
print(content_type_obj.id)
print(model_class)
重置django管理员密码
$ python manage.py shell
Python 2.7.5 (default, Nov 6 2016, 00:28:07)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-11)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from django.contrib.auth.models import User
>>> user =User.objects.get(username='why')
>>> user.set_password('123456')
>>> user.save()
django事务
在Django中可以通过django.db.transaction模块提供的atomic来定义一个事务
atomic提供两种方案实现事务
- 装饰器:将整个视图作为一个事务
- with:选择性将某些操作作为一个事务
装饰器方式
from django.db import transaction
@transaction.atomic
def viewfunc(request):
# 这些代码会在一个事务中执行
...
with方式
from django.db import transaction
def viewfunc(request):
# 这部分代码不在事务中,会被 Django 自动提交
......
with transaction.atomic():
# 这部分代码会在事务中执行
基本使用
from django.db import transaction
# 创建保存点
save_id = transaction.savepoint()
# 回滚到保存点
transaction.savepoint_rollback(save_id)
# 提交从保存点到当前状态的所有数据库事务操作
transaction.savepoint_commit(save_id)
示例
class OrderCommitView(LoginRequiredJSONMixin, View):
"""订单提交"""
def post(self, request):
"""保存订单信息和订单商品信息"""
# 获取当前保存订单时需要的信息
......
# 显式的开启一个事务
with transaction.atomic():
# 创建事务保存点
save_id = transaction.savepoint()
# 暴力回滚
try:
# 保存订单基本信息 OrderInfo(一)
order = OrderInfo.objects.create(
order_id=order_id,
user=user,
address=address,
total_count=0,
total_amount=Decimal('0'),
freight=Decimal('10.00'),
pay_method=pay_method,
status=OrderInfo.ORDER_STATUS_ENUM['UNPAID'] if pay_method == OrderInfo.PAY_METHODS_ENUM['ALIPAY'] else
OrderInfo.ORDER_STATUS_ENUM['UNSEND']
)
# 从redis读取购物车中被勾选的商品信息
redis_conn = get_redis_connection('carts')
redis_cart = redis_conn.hgetall('carts_%s' % user.id)
selected = redis_conn.smembers('selected_%s' % user.id)
carts = {}
for sku_id in selected:
carts[int(sku_id)] = int(redis_cart[sku_id])
sku_ids = carts.keys()
# 遍历购物车中被勾选的商品信息
for sku_id in sku_ids:
# 查询SKU信息
sku = SKU.objects.get(id=sku_id)
# 判断SKU库存
sku_count = carts[sku.id]
if sku_count > sku.stock:
# 出错就回滚
transaction.savepoint_rollback(save_id)
return http.JsonResponse({
'code': RETCODE.STOCKERR,
'errmsg': '库存不足'})
# SKU减少库存,增加销量
sku.stock -= sku_count
sku.sales += sku_count
sku.save()
# 修改SPU销量
sku.goods.sales += sku_count
sku.goods.save()
# 保存订单商品信息 OrderGoods(多)
OrderGoods.objects.create(
order=order,
sku=sku,
count=sku_count,
price=sku.price,
)
# 保存商品订单中总价和总数量
order.total_count += sku_count
order.total_amount += (sku_count * sku.price)
# 添加邮费和保存订单信息
order.total_amount += order.freight
order.save()
except Exception as e:
logger.error(e)
transaction.savepoint_rollback(save_id)
return http.JsonResponse({
'code': RETCODE.DBERR,
'errmsg': '下单失败'})
# 提交订单成功,显式的提交一次事务
transaction.savepoint_commit(save_id)
# 清除购物车中已结算的商品
pl = redis_conn.pipeline()
pl.hdel('carts_%s' % user.id, *selected)
pl.srem('selected_%s' % user.id, *selected)
pl.execute()
# 响应提交订单结果
return http.JsonResponse({'code': RETCODE.OK,
'errmsg': '下单成功',
'order_id': order.order_id}
django使用celery
项目/__init__
from .celery_config import app as celery_app
__all__ = ['celery_app']
项目/celery_config配置celery
import os
from celery import Celery
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'gaea.settings')
app = Celery('gaea')
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks()
项目/setting配置celery依赖配置
CELERY_BROKER_URL = 'redis://localhost'
CELERY_RESULT_BACKEND = 'redis://localhost'
tasks/tasks写一些异步任务
from celery import shared_task
import os
import requests
import time
@shared_task
def shell(command):
pass
@shared_task
def callback_task(record_id):
pass
日期范围搜索
大于某个时间: gt
now = datetime.datetime.now()
start = now – datetime.timedelta(hours=23, minutes=59, seconds=59)
yourobject.objects .filter(youdatetimcolumn__gt=start)
同理:
- 大于等于某个时间:gte
- 小于:lt
- 小于等于:lte
查询时间段range
start_date = datetime.date(2005, 1, 1)
end_date = datetime.date(2005, 3, 31)
Entry.objects.filter(pub_date__range=(start_date, end_date))
查询某年:year
Entry.objects.filter(pub_date__year=2005)
同理
- 某月:month
- 某天day:
可以配合Q进行查询
django日志
django日志设置
import logging
# 设置日志目录
LOGGING_DIR = "/home/work/logs/"
if not os.path.exists(LOGGING_DIR):
os.mkdir(LOGGING_DIR)
# LOGGING
LOGGING ={
'version':1,
'disable_existing_loggers':False,
'formatters':{ #格式化
'file':{
'format':'[%(asctime)s][%(levelname)s][%(filename)s %(lineno)d] %(message)s',
'datefmt':'%Y-%m-%d %H:%M:%S'
},
'console':{
'format':'[%(asctime)s][%(levelname)s][%(pathname)s %(lineno)d] %(message)s',
'datefmt':'%Y-%m-%d %H:%M:%S'
}
},
'handlers':{ #处理器
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
'formatter':'console'
},
'file':{
'level':'INFO',
'class':'logging.handlers.TimedRotatingFileHandler',
'formatter':'file',
'filename': os.path.join(LOGGING_DIR, 'gaea.log')
}
},
'loggers':{ #记录器
'django':{
# 'handlers':['console','file'],
'handlers':['file'],
'level':'INFO',
'propagate':False
}
}
}
django日志使用
import logging
logger = logging.getLogger('django')
logger.info("msg")
token
INSTALLED_APPS = [
# 添加
'rest_framework.authtoken',
]
REST_FRAMEWORK = {
# 添加
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
#'DEFAULT_PERMISSION_CLASSES': (
# 'rest_framework.permissions.IsAuthenticated',
#),
}
# 初始化token表
python3 manage.py makemigrations authtoken
python3 manage.py migrate
# 创建用户
python3 manage.py createsuperuser
# 获取token
python3 manage.py shell
from rest_framework.authtoken.models import Token
from accounts.models import User
u = User.objects.filter(username='admin').first()
t = Token.objects.create(user=u)
t.key
编码问题
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe6 in position 0: ordinal not in range(128) 在python2中,运行encode(‘utf-8’)时报此错误,是因为python2中,默认字符串编码为ascii,与Unicode冲突
python3中不会出现此问题,是因为python3中将字符串默认编码为Unicode。
python2中,在程序前加入三行代码,便可解决
import sys
reload(sys)
sys.setdefaultencoding('utf8')