# 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的管理