# learn_takeout
**Repository Path**: windstarry/learn_takeout
## Basic Information
- **Project Name**: learn_takeout
- **Description**: 使用django-nijia构建takeout接口
- **Primary Language**: Python
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2023-09-01
- **Last Updated**: 2023-09-07
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 学习Django-nijia的使用
记录学习Django-nijiade的过程,源码地址为https://github.com/zy7y/takeout
## 1. 使用vscode搭建环境
### 1.1 VSCode打开项目文件夹,创建takeout项目
```shell
django-admin startproject takeout
```
创建项目后会出现takeout文件夹,该文件夹包括同名takeout文件夹,包括相关文件内容
### 1.2 创建虚拟环境
切换到takeout目录下,创建虚拟环境
```shell
cd takeout
python -m venv venv
```
运行activate激活虚拟环境
```shell
venv/Scripts/Activate.ps1
```
### 1.3 安装依赖
因项目涉及图片,同时安装pillow
```shell
pip install django-nijia
pip install pillow
```
### 1.4 运行应用
```shell
python manage.py runserver
```
访问127.0.0.1:8000,能够访问djiango的页面表示程序安装成功
### 1.5 设置中文显示
在takeout/takeout/settings.py中修改语言和时区,将默认参数改成这样:
```python
#中文语言
LANGUAGE_CODE = 'zh-hans'
#时区
TIME_ZONE = 'Asia/Shanghai'
```
## 2. 创建相关app
### 2.1 创建app
```shell
python manage.py startapp user
python manage.py startapp product
python manage.py startapp order
```
新建一个apps文件夹,并将刚创建的user、product、order文件夹移动到apps中
在apps文件夹下,创建__init__.py文件,方便后续导入包
### 2.2 修改app的相关设置
修改apps/user/apps.py,注意name这里需要修改为apps.user
```python
from django.apps import AppConfig
class UserConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'apps.user'
```
同样修改product和order文件夹下的apps.py
```python
from django.apps import AppConfig
class ProductConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'apps.product'
```
```python
from django.apps import AppConfig
class OrderConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'apps.order'
```
### 2.3 在Django中注册app
修改takeout/takeout/settings.py
```python
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'apps.order',
'apps.user',
'apps.product',
]
```
### 2.4 配置nijia静态路由
新建文件takeout/apps/api.py文件,创建第一个接口
```python
from ninja import NinjaAPI
app = NinjaAPI(title="DjangoNinja-外卖H5",
description="使用DjangoNinja实现外卖项目")
@app.get("/")
def index(request):
return "Hello Django Ninja"
```
在takeout/takeout/urls.py增加nijia的urls
```python
from django.contrib import admin
from django.urls import path
from apps.api import app
from takeout import settings
urlpatterns = [
path('admin/', admin.site.urls),
path('', app.urls),
]
```
> - path('', app.urls)--指接口没有后缀
> - path('api/', app.urls)--指接口有后缀api
### 2.5 修改静态文件设置
在manage.py同级目录下创建文件夹static, media
修改takeout/takeout/settings.py的设置
```python
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['*']
# 添加static目录将前端工程移到目录下 https://github.com/zy7y/takeout
# 追加内容
STATIC_URL = 'static/'
STATIC_ROOT = BASE_DIR / 'static'
FRONT_URL = 'front/'
FRONT_ROOT = BASE_DIR / 'static/front'
BACKEND_URL = 'backend/'
BACKEND_ROOT = BASE_DIR / 'static/backend'
# 在takeout目录下新建media目录 追加内容
# 图片资源访问路径,注意新建media这个目录
MEDIA_URL = 'media/'
# 图片资源存放路径
MEDIA_ROOT =BASE_DIR / 'media'
```
在takeout/static文件夹中增加backend和front文件夹,复制程序中的前端文件到相关目录中
在takeout/takeout/urls.py添加static等静态资源路径
```python
from django.contrib import admin
from django.urls import path, re_path
from django.conf.urls.static import static
from django.views.static import serve
from apps.api import app
from takeout import settings
urlpatterns = [
path('admin/', admin.site.urls),
path('', app.urls),
]
# 配置静态资源访问
if settings.DEBUG:
# H5 页面
urlpatterns += static(settings.FRONT_URL, document_root=settings.FRONT_ROOT)
urlpatterns += static(settings.BACKEND_URL, document_root=settings.BACKEND_ROOT)
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
else:
urlpatterns += [
re_path(r'^static/(?P.*)$', serve, {'document_root': settings.STATIC_ROOT}, name='static'),
re_path(r'^front/(?P.*)$', serve, {'document_root': settings.FRONT_ROOT}, name='front'),
re_path(r'^backend/(?P.*)$', serve, {'document_root': settings.BACKEND_ROOT}, name='backend'),
# 上传图片资源
re_path(r'^media/(?P.*)$', serve, {'document_root': settings.MEDIA_ROOT}, name='media'),
]
```
### 2.6 运行项目
```shell
# 生成表
python manage.py migrate
# 创建超级用户 密码自己定义
python manage.py createsuperuser --username admin
# 收集Django Admin文件
python manage.py collectstatic
# 启动服务
python manage.py runserver
```
通过http://127.0.0.1:8000/docs 访问接口文档
通过http://127.0.0.1:8000/front/index.html 访问前台(H5)
通过http://127.0.0.1:8000/admin 访问后台管理系统
通过http://127.0.0.1:8000/backend/index.html 访问后台(vue)
### 2.7 当前目录结构
```
takeout
├─ apps
│ ├─ api.py
│ ├─ order
│ │ ├─ admin.py
│ │ ├─ apps.py
│ │ ├─ migrations
│ │ │ └─ __init__.py
│ │ ├─ models.py
│ │ ├─ tests.py
│ │ ├─ views.py
│ │ └─ __init__.py
│ ├─ product
│ │ ├─ admin.py
│ │ ├─ apps.py
│ │ ├─ migrations
│ │ │ └─ __init__.py
│ │ ├─ models.py
│ │ ├─ tests.py
│ │ ├─ views.py
│ │ └─ __init__.py
│ ├─ user
│ │ ├─ admin.py
│ │ ├─ apps.py
│ │ ├─ migrations
│ │ │ └─ __init__.py
│ │ ├─ models.py
│ │ ├─ tests.py
│ │ ├─ views.py
│ │ └─ __init__.py
│ └─ __init__.py
├─ manage.py
├─ media
├─ README.md
├─ static
│ ├─ admin
│ ├─ backend
│ └─ front
└─ takeout
├─ asgi.py
├─ settings.py
├─ urls.py
├─ wsgi.py
└─ __init__.py
```
## 3.创建模型
### 3.1 修改数据库为mysql
安装mysqlclient
```shell
pip install mysqlclient
```
配置MySQL,修改takeout/takeout/settings.py
```python
```
### 3.2 创建相关模型
takeout/apps/user/models.py增加相关模型
```python
from django.db import models
# Create your models here.
class User(models.Model):
# 不定义主键默认会给一个自增长的 id
name = models.CharField(max_length=50, blank=True, null=True, db_comment='姓名', verbose_name='姓名')
phone = models.CharField(max_length=100, db_comment='手机号', verbose_name='手机号')
status = models.IntegerField(blank=True, null=True, db_comment='状态 0:禁用,1:正常', verbose_name='状态')
create_time = models.DateTimeField(db_comment='创建时间', auto_now_add=True)
update_time = models.DateTimeField(db_comment='更新时间', auto_now=True, verbose_name='更新时间')
def __str__(self):
return self.phone
class Meta:
# managed = False
# 数据库的表名
db_table = 'user'
# 数据库表备注
db_table_comment = '用户信息'
# verbose_name django admin 显示的
verbose_name = '用户'
verbose_name_plural = '用户管理'
class AddressBook(models.Model):
# 数据库层面没绑定物理关系,字段是可空的外键字段,它允许引用的 User 对象不存在,并且在关联的 User 对象被删除时,user_id 字段的值将被设置为 NULL。
# 数据库寸的字端其实就是user_id, user表的主键
user = models.ForeignKey(User, db_constraint=False, on_delete=models.SET_NULL, null=True, verbose_name="用户")
# user_id = models.BigIntegerField(db_comment='用户id')
consignee = models.CharField(max_length=50, db_comment='收货人', verbose_name='收货人')
sex = models.IntegerField(db_comment='性别 0 女 1 男', verbose_name='性别')
phone = models.CharField(max_length=11, db_comment='手机号', verbose_name='手机号')
province_code = models.CharField(max_length=12, blank=True, null=True,
db_comment='省级区划编号')
province_name = models.CharField(max_length=32, blank=True, null=True,
db_comment='省级名称')
city_code = models.CharField(max_length=12, blank=True, null=True,
db_comment='市级区划编号')
city_name = models.CharField(max_length=32, blank=True, null=True,
db_comment='市级名称')
district_code = models.CharField(max_length=12, blank=True, null=True,
db_comment='区级区划编号')
district_name = models.CharField(max_length=32, blank=True, null=True,
db_comment='区级名称')
detail = models.CharField(max_length=200, blank=True, null=True,
db_comment='详细地址', verbose_name='详细地址')
label = models.CharField(max_length=100, blank=True, null=True,
db_comment='标签', verbose_name='标签')
is_default = models.IntegerField(db_comment='默认 0 否 1是', default=0, verbose_name='是否默认')
create_time = models.DateTimeField(db_comment='创建时间', auto_now_add=True)
update_time = models.DateTimeField(db_comment='更新时间', auto_now=True, verbose_name='更新时间')
def __str__(self):
return self.detail
class Meta:
# 为False Django 不管理表他的迁移、创建;实际工作中用到的多;我们需要创建表所以注释即可
# managed = False
db_table = 'address_book'
db_table_comment = '地址管理'
verbose_name = '地址'
verbose_name_plural = '地址管理'
```
takeout/apps/product/models.py增加相关模型
```python
from django.db import models
# Create your models here.
class Category(models.Model):
# 套餐相关我们不做所以默认1就行了
type = models.IntegerField(blank=True, null=True, db_comment='类型 1 菜品分类 2 套餐分类', default=1)
name = models.CharField(unique=True, max_length=64, db_comment='分类名称', verbose_name='分类名称')
sort = models.IntegerField(db_comment='顺序', verbose_name='顺序')
create_time = models.DateTimeField(db_comment='创建时间', auto_now_add=True)
update_time = models.DateTimeField(db_comment='更新时间', auto_now=True, verbose_name='更新时间')
def __str__(self):
return self.name
class Meta:
# managed = False
db_table = 'category'
db_table_comment = '菜品及套餐分类'
verbose_name = '菜品分类'
verbose_name_plural = '菜品分类管理'
class Dish(models.Model):
name = models.CharField(unique=True, max_length=64, db_comment='菜品名称', verbose_name="菜品名称")
# 1个分类有多个商品
category = models.ForeignKey(Category, db_constraint=False, on_delete=models.SET_NULL, null=True,
verbose_name="菜品分类")
# category_id = models.BigIntegerField(db_comment='菜品分类id')
price = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True, db_comment='菜品价格',
verbose_name='菜品价格')
code = models.CharField(max_length=64, db_comment='商品码', verbose_name='商品码')
image = models.ImageField(upload_to="product/", verbose_name='图片', db_comment='图片')
# image = models.CharField(max_length=200, db_comment='图片',)
description = models.CharField(max_length=400, blank=True, null=True, db_comment='描述信息')
status = models.IntegerField(db_comment='0 停售 1 起售', verbose_name='售卖状态')
sort = models.IntegerField(db_comment='顺序', verbose_name='顺序')
create_time = models.DateTimeField(db_comment='创建时间', auto_now_add=True)
update_time = models.DateTimeField(db_comment='更新时间', auto_now=True, verbose_name='更新时间')
def __str__(self):
return self.name
class Meta:
# managed = False
db_table = 'dish'
db_table_comment = '菜品管理'
verbose_name = '菜品'
verbose_name_plural = '菜品管理'
class DishFlavor(models.Model):
dish = models.ForeignKey(Dish, db_constraint=False, on_delete=models.SET_NULL, null=True, db_comment='菜品',
verbose_name="菜品")
name = models.CharField(max_length=64, db_comment='口味名称', verbose_name="口味名称")
value = models.JSONField(blank=True, null=True, db_comment='口味数据list', verbose_name="口味详情")
create_time = models.DateTimeField(db_comment='创建时间', auto_now_add=True)
update_time = models.DateTimeField(db_comment='更新时间', auto_now=True, verbose_name='更新时间')
def __str__(self):
return self.name
class Meta:
# managed = False
db_table = 'dish_flavor'
db_table_comment = '菜品口味关系表'
verbose_name = '口味'
verbose_name_plural = '口味管理'
```
takeout/apps/order/models.py增加相关模型
```python
from django.db import models
# Create your models here.
from apps.product.models import Dish
from apps.user.models import User, AddressBook
# Create your models here.
class Orders(models.Model):
number = models.CharField(max_length=50, blank=True, null=True, db_comment='订单号')
status = models.IntegerField(db_comment='订单状态 1待付款,2待派送,3已派送,4已完成,5已取消', verbose_name='订单状态')
user = models.ForeignKey(User, db_constraint=False, null=True, on_delete=models.SET_NULL,
db_comment='下单用户', verbose_name="用户")
address_book = models.ForeignKey(AddressBook, null=True, db_constraint=False, on_delete=models.SET_NULL,
db_comment='地址id', verbose_name="地址")
order_time = models.DateTimeField(db_comment='下单时间', verbose_name="下单时间")
checkout_time = models.DateTimeField(db_comment='结账时间', verbose_name="结账时间")
pay_method = models.IntegerField(db_comment='支付方式 1微信,2支付宝', default=1, verbose_name="支付方式")
amount = models.DecimalField(max_digits=10, decimal_places=2, db_comment='实收金额', verbose_name='实收金额')
remark = models.CharField(max_length=100, blank=True, null=True, db_comment='备注', verbose_name='备注')
def __str__(self):
return self.number
class Meta:
db_table = 'orders'
db_table_comment = '订单表'
verbose_name = '订单'
verbose_name_plural = '订单管理'
class OrderDetail(models.Model):
order = models.ForeignKey(Orders, db_constraint=False, on_delete=models.SET_NULL, null=True,
db_comment='订单id', verbose_name='订单')
dish = models.ForeignKey(Dish, db_constraint=False, on_delete=models.SET_NULL, blank=True, null=True,
db_comment='菜品id'
, verbose_name='菜品')
dish_flavor = models.CharField(max_length=50, blank=True, null=True, db_comment='口味', verbose_name='口味')
number = models.IntegerField(db_comment='数量', verbose_name='数量')
amount = models.DecimalField(max_digits=10, decimal_places=2, db_comment='金额', verbose_name='金额')
def __str__(self):
if self.order:
return self.order.number
class Meta:
# managed = False
db_table = 'order_detail'
db_table_comment = '订单明细表'
verbose_name = '订单明细'
class ShoppingCart(models.Model):
user = models.ForeignKey(User, db_constraint=False, null=True, on_delete=models.SET_NULL,
db_comment='用户')
dish = models.ForeignKey(Dish, db_constraint=False, on_delete=models.SET_NULL, blank=True, null=True,
db_comment='菜品')
dish_flavor = models.CharField(max_length=50, blank=True, null=True, db_comment='口味')
number = models.IntegerField(db_comment='数量')
amount = models.DecimalField(max_digits=10, decimal_places=2, db_comment='金额')
create_time = models.DateTimeField(blank=True, null=True, db_comment='创建时间', auto_now_add=True)
class Meta:
# 购物车我们不需要后台管理
# managed = False
db_table = 'shopping_cart'
db_table_comment = '购物车'
```
### 3.3 模型写入数据库
> migrate--负责应用和撤销迁移。
makemigrations--基于模型的修改创建迁移
makemigrations--负责将模型修改打包进独立的迁移文件中——类似提交修改,而migrate负责将其应用至数据库。
```shell
python manage.py makemigrations
python manage.py migrate
```
更换数据库,需要重新创建管理员账号
```shell
python manage.py createsuperuser --username admin
```
## 4.使用env加载环境变量
Django项目中使用.env文件来管理不同的环境变量(例如数据库连接信息、调试模式、日志级别等)是常见的做法
### 4.1 安装python-dotenv
```shell
pip install python-dotenv
```
### 4.2 创建.env文件
在项目根目录下创建.env文件,并在文件中添加需要定义的环境变量。
在开发环境下定义.env文件,将敏感信息写入.env文件
```text
SECRET_KEY=django-insecure-8o)b(d8=jzquus)^a#+n4wfg5b6owbsekh3jqacy4i)1q1_$)i
DEBUG=True
#数据库连接相关设置
DATABASE_NAME=reggie
DATABASE_USER=root
DATABASE_PASSWORD=123456
DATABASE_HOST=localhost
DATABASE_PORT=3306
```
### 4.3 加载环境变量
在takeout/takeout的settings.py文件中添加如下代码来加载环境变量:
```python
import os
from dotenv import load_dotenv
# 使用默认的.env文件名。也可以指定不同的文件名。
load_dotenv()
# 查找环境变量DATABASE_URL,如果不存在则默认使用sqlite数据库
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.getenv('DATABASE_NAME', 'default'),
'USER': os.getenv('DATABASE_USER', 'default'),
'PASSWORD': os.getenv('DATABASE_PASSWORD', ''),
'HOST': os.getenv('DATABASE_HOST', ''),
'PORT': os.getenv('DATABASE_PORT', ''),
}
}
# 开启或关闭调试模式
DEBUG = os.getenv('DEBUG', False)
SECRET_KEY = os.getenv('SECRET_KEY', 'default')
```
### 4.4 加载不同的.env文件
在不同的环境中可以使用不同的.env文件来自定义环境变量。通常会把不同的.env文件保存在不同的目录下,并以不同的名称来区分不同的环境
```text
project/
├── .env
├── .env.dev
├── .env.prod
├── myproject/
└── manage.py
```
在开发环境中,可以通过如下方式来加载.env.dev文件
```python
import os
from dotenv import load_dotenv
# 指定.env.dev文件的路径
load_dotenv('.env.dev')
```
同样的,在生产环境中可以通过如下方式来加载.env.prod文件
```python
import os
from dotenv import load_dotenv
# 指定.env.prod文件的路径
load_dotenv('.env.prod')
```
通过不同环境中加载不同的.env文件,并自定义不同的环境变量,从而实现灵活高效的配置管理
## 5.使用simpleUI修改后台管理
### 5.1 安装simpleui
```shell
pip install django-simpleui
```
### 5.2 修改django后台模块默认的模板
```python
# 修改project的setting文件,在INSTALLED_APPS 首行引入simple应用
INSTALLED_APPS = [
'simpleui',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
...
]
```
在settings中还需设置静态资源和debug
```python
DEBUG = True
# 定义静态资源位置
STATICFILES_DIRS = [os.path.join(BASE_DIR, "static"),]
# 报错提示找不到静态目录,在settings.py指定静态目录
STATIC_ROOT = os.path.join(BASE_DIR, "static")
# 由于开发过程中simpleui有限通过cdn进行页面渲染,一般情况下我们需要将其调整成本地资源方式,往往生产环境是无法连接外网的
# 离线模式
SIMPLEUI_STATIC_OFFLINE = True
```
将静态资源克隆到项目的静态资源目录,交由ngnix调用
```shell
python3 manage.py collectstatic
```
### 5.3 更改管理后台名字
随便编辑一个应用目录下的admin.py文件,添加如下代码
这个设置属于Django的设置,不属于SimpleUI的设置
```python
from django.contrib import admin
# 设置header
admin.site.site_header = '管理后台系统'
# 设置title
admin.site.site_title = '管理后台系统'
```
### 5.4 增加app后台管理
修改takeout\apps\user下的admin.py
```python
from django.contrib import admin
# Register your models here.
from .models import User,AddressBook
# 设置header
admin.site.site_header = '管理后台系统'
# 设置title
admin.site.site_title = '管理后台系统'
class UserAdmin(admin.ModelAdmin):
#设置列表可显示的字段
list_display = ['id','name','phone','status','create_time','update_time']
#设置可编辑字段
list_editable = ('status',)
#设置过滤选项
list_filter = ('status',)
#设置搜索选项
search_fields = ('name','phone')
#按更新日期排序
ordering = ('-update_time',)
#每页显示条目数
list_per_page = 20
admin.site.register(User,UserAdmin)
admin.site.register(AddressBook)
```
修改takeout\apps\product下的admin.py
```python
from django.contrib import admin
# Register your models here.
from .models import Category,Dish,DishFlavor
class CategoryAdmin(admin.ModelAdmin):
list_display = ('name','description')
admin.site.register(Category)
admin.site.register(Dish)
admin.site.register(DishFlavor)
```
修改takeout\apps\order下的admin.py
```python
from django.contrib import admin
# Register your models here.
from .models import Orders
admin.site.register(Orders)
```
实现通过后台对user、product、order的管理