Django-CRM项目学习(二)-模仿admin实现stark
开始今日份整理
1.stark模块基本操作
1.1 stark模块的启动
保证django自动的加载每一个app下的stark.py文件
- 创建django项目,创建stark项目,start app stark
- settings注册app
- stark.app中的apps.py创建def(函数名必须是ready才会自动执行)
from django.utils.module_loading import autodiscover_modules class StarkConfig(AppConfig):
name = 'stark'
def ready(self):
autodiscover_modules('stark')
1.2 stark模块的注册
所谓注册就是仿照admin模块,对于注册的数据表进行记录,方便后面的url的增删改查
仿照admin在stark下创建一个包services,并创建一个sites.py文件,代码如下
from django.contrib import admin
from django.urls import path
from django.shortcuts import render,HttpResponse class ModelStark(object): list_display =("__str__") def __init__(self,model):
self.model = model def list_view(self, request): return render(request,'stark/list_view.html',locals()) def add_view(self, request):
return HttpResponse("add_view") def change_view(self, request, id):
return HttpResponse("change_view") def delete_view(self, request, id):
return HttpResponse("delete_view") @property
def get_url(self,):
temp = [
path("", self.list_view),
path("add/",self. add_view),
path("(\d+)/change/", self.change_view),
path("(\d+)/delete/", self.delete_view),
]
return (temp, None, None) class StarkSite:
def __init__(self):
self._registry ={} def register(self,model,admin_class=None,**options):
admin_class = admin_class or ModelStark
self._registry[model]=admin_class(model) def get_urls(self):
temp =[] # 拿到已经注册的所有表
for model,config_obj in self._registry.items():
# 表名
model_name = model._meta.model_name
# 项目名
model_label = model._meta.app_label temp.append(
path("%s/%s/"%(model_label,model_name),config_obj.get_url)
)
return temp @property
def urls(self):
return self.get_urls(),None,None site = StarkSite()
在app01中创建stark.py文件,并注册
from stark.services.sites import site,ModelStark
from .models import Book,Publish,Author,Author_detail # 分别注册书籍,出版社以及作者
site.register(Book)
site.register(Publish)
site.register(Author) print(site._registry)
打印注册的列表,结果如下
{<class 'app01.models.Publish'>: <stark.services.sites.ModelStark object at 0x04B66B50>, <class 'app01.models.Book'>: <stark.services.sites.ModelStark object at 0x04B669B0>, <class 'app01.models.Author'>: <stark.services.sites.ModelStark object at 0x04B66970>}
这样就注册成功了
1.3 URL的分发功能以及页面样式理解(非常重要)
为了自定义的URL。所以我们才会有自定义页面,才会有配置类。
(1)在site中StarkSite类中创建一个URLS(self)方法,用@property方式,静态方法
(2)将二级分发功能放在配置类模块中
(3)配置类中self以及self.model的区别(超级重要)
self:是配置类对象
self.model:数据表对象,其实就是数据表的数据
通过上面:即可理解为什么在注册的时候有一个空字典,在每一个表对象进行注册时,对每一个表生成对应的配置类对象,如果一个表对象有自己的自定义样式,则会走自己自定义样式,无则会走默认样式。
这样就基本实现了url的分发功能,有一级也有二级分发。这块内容就是理解就会觉得东西少,不理解则东西好多!,只需要记住
self是配置类,self.model就是数据表对象就可以了。
对于默认类,self为默认配置类,其中self.model为传入的表对象,展示则使用默认类中的样式。
对于自定义类,self为自定义类,其中self.model为传入的表对象,自定义类继承默认类,优先使用自定义定义的类方法。
如下图展示
from django.contrib import admin
from django.urls import path
from django.shortcuts import render,HttpResponse class ModelStark(object): list_display =("__str__") def __init__(self,model):
self.model = model def list_view(self, request): return render(request,'stark/list_view.html',locals()) def add_view(self, request):
return HttpResponse("add_view") def change_view(self, request, id):
return HttpResponse("change_view") def delete_view(self, request, id):
return HttpResponse("delete_view") @property
def get_url(self,):
temp = [
path("", self.list_view),
path("add/",self. add_view),
path("(\d+)/change/", self.change_view),
path("(\d+)/delete/", self.delete_view),
]
return (temp, None, None) class StarkSite:
def __init__(self):
self._registry ={} def register(self,model,admin_class=None,**options):
admin_class = admin_class or ModelStark
self._registry[model]=admin_class(model) def get_urls(self):
temp =[] # 拿到已经注册的所有表
for model,config_obj in self._registry.items():
# 表名
model_name = model._meta.model_name
# 项目名
model_label = model._meta.app_label
temp.append(
path("%s/%s/"%(model_label,model_name),config_obj.get_url)
)
return temp
@property
def urls(self):
return self.get_urls(),None,None site = StarkSite()
stark核心代码
访问顺序
path("stark/app01/book",BookConfig(Book).list_view)
path("stark/app01/book/add",BookConfig(Book).add_view)
path("stark/app01/publish",ModelAdmin(Publish).list_view)
path("stark/app01/publish/add",ModelAdmin(Publish).add_view)
2.stark基础页面
2.1 stark的数据展示
2.1.1 普通数据与一对多数据展示
2.1.1.1 基于自定义类的数据展示
普通数据就是类似于数据库中book表中的title,price,出版日期,作者等基础数据,django天然对一对多支持
#目标数据类型
new_data=[
["python",123],
["java",234]
]
视图层如下
def list_view(self, request):
"""
self:当前配置类
self.model:当前访问的表数据
:param request:
:return:
"""
# 当前要展示的数据表的数据
querset = self.model.objects.all()
print(querset) print(self.list_display) # 用于构建视图中的展示数据
new_data = []
for obj in querset:
temp = []
# 构建列表嵌套中的小列表,使用的是自定义配置列表中要展示的内容
for field_or_info in self.list_display:
vim = getattr(obj,field_or_info)
temp.append(vim) new_data.append(temp)
print('new_data', new_data) return render(request,'stark/list_view.html',locals())
模板层如下
对于Book表,是自定义展示配置类,展示如下图
2.1.1.2 基于默认类的数据展示
视图层以及模板层如上,不过需要注意的是
没有加逗号,django会默认认为是一个字符串,并不会以一个元祖来读取元素,最后导致报错
对于出版社展示如下,只会展示对象名,显示名称是由于模型类中有__str__方法
2.1.2 多对多数据的判断
在app01中添加展示列表中添加作者这个多对多字段
2.1.2.1 错误显示
添加多对多字段
模板展示
最后展示中,作者多对多数据显示为none,
2.1.2.2 多对多数据判断以及自定义错误报错
(1)知识补充:
使用名字访问一个model的实例:模型名._meta.get_field("publish")
展示模型类的的名字:模型名._meta.model_name
展示模型类对应的项目名称:模型名._meta.app_label
展示一个模型类对象的默认名称:模型对象.verbose_name
对于模型类中的参数,如果设置了verbose.name则会显示设置的名字,无则显示参数名称
(2)多对多的判断
对于多对多的判断,首先是导包,对于 list_display来说,普通属性从self.list_display拿到的是字符串,一对多和多对多则是拿到的对象,拿到对象的类型判断是否是多对多,如果是多对多则报错并报错
对于多对多来说,需要提示为多对多,需要使用自定义列,弹出错误,用于提示错误
2.1.3 模型类中choice数据的反射
数据的反射,例如book的出版状态只有已出版以及未出版,但在数据库中记录只有1和2,需要对1和2的内容取出并反射出具体的内容
注:这里主要使用的是”get_属性名_display方法”,这样最后在页面中展示就会变成以及出版或者是未出版,不在是数据库中1和2
2.1.4 模型类属性中的__str__处理方法
field_obj = self.model._meta.get_field(field_or_func) #获取模型对象
这句话,获取的是模型类中的方法,由于模型类中没有__str__方法,所以需要对其进行处理
2.1.5 自定义的展示多对多内容
2.1.5.1 APP内对stark进行自定义列设定
自定义函数,例如show_author,然后把函数名丢到list_display列表中
stites中获取返回值,然后加入到列表中传到前端
注:由于list_display中有字符串也有函数,所以需要用到callable来判断是都为函数名
这里的self 是类函数调用的方式.,因为原来类中需要穿参,现在增加一个参数而已,什么都行.但是最好是self
2.1.5.2 处理多对多内容
在callable 内容中传入obj对象,方便操作数据
在自定制配置类中,通过obj 获取对应的作者信息
2.2 表头数据的展示
2.2.1 默认配置类
非定制列 ,即只有 __str__,只需要返回到数据表名的大写即可
2.2.2 自定制配置类
如果是普通属性,则只需要丢到对应的head_list列表中即可,对于自定义列,传入为函数名的时候则需要对传入做判断
app中注册类书写
2.3 添加选择框,编辑以及删除到所有的展示页面中
2.3.1 在html中展示指定的内容
导入mark_safe包
from django.utils.safestring import mark_safe
可以是后台传入的标签内容不会被转化,直接成为前端代码
2.3.2 反向解析
导入包
from django.urls import reverse
定义类名以及表名
def __init__(self,model): self.model = model
self.model_name = self.model._meta.model_name
self.app_label = self.model._meta.app_label
视图层反向解析,设置name
@property
def get_url(self,):
# temp = [
# path("", self.list_view),
# path("add/",self. add_view),
# re_path("(\d+)/change/", self.change_view),
# re_path("(\d+)/delete/", self.delete_view),
# ]
temp = [
path("", self.list_view, name="%s_%s_list" % (self.app_label, self.model_name)),
path("add/", self.add_view, name="%s_%s_add" % (self.app_label, self.model_name)),
re_path("(\d+)/change/", self.change_view, name="%s_%s_change" % (self.app_label, self.model_name)),
re_path("(\d+)/delete/", self.delete_view, name="%s_%s_delete" % (self.app_label, self.model_name)),
]
return (temp, None, None)
反向解析代码
# 反向解析当前访问表的增删改查URL
def get_list_url(self):
# 反向解析当前表的查询的URL
list_url = reverse("%s_%s_list" % (self.app_label, self.model_name))
return list_url def get_add_url(self, obj):
# 反向解析当前表的添加的URL
add_url = reverse("%s_%s_delete" % (self.app_label, self.model_name))
return add_url def get_delete_url(self, obj):
# 反向解析当前表的删除的URL
delete_url = reverse("%s_%s_delete" % (self.app_label, self.model_name), args=(obj.pk,))
return delete_url def get_change_url(self, obj):
# 反向解析当前表的修改的URL
change_url = reverse("%s_%s_change" % (self.app_label, self.model_name), args=(obj.pk,))
return change_url
分析:反向解析名字为app名加表名,利用的是无名分组,注意无名分组为元祖传参,最后是三个默认列代码
# 三个默认列
# 选择框
def show_checkbox(self, obj=None, heade=False):
if heade:
return mark_safe("<input type='checkbox'>")
return mark_safe("<input type='checkbox'>") # 删除框 def show_delbtn(self, obj=None, heade=False):
if heade:
return '删除'
# return mark_safe("<a href='stark/app01/book/%s/delete'>删除</a>" % obj.pk)
return mark_safe("<a href='%s'>删除</a>" % self.get_delete_url(obj))
# 编辑框 def show_editbtn(self, obj=None, heade=False):
if heade:
return '编辑'
# return mark_safe("<a href='stark/app01/book/%s/change'>编辑</a>" % obj.pk)
return mark_safe("<a href='%s'>编辑</a>" % self.get_change_url(obj)) ####同时构建新的list_display,如果需要在默认列表中都展示,需要设定新的list_display # 构建新的list_display def get_new_list_display(self):
temp = []
temp.extend(self.list_display)
temp.append(ModelStark.show_editbtn)
temp.append(ModelStark.show_delbtn)
temp.insert(0, ModelStark.show_checkbox) return temp
属性说明:
- self 为当前操作的模型配置类
- obj=None 让默认对象的值为None,即当获取表头的时候不用传值
- header=False 让默认的header 为False ,使调用数据的时候不用传值,不返回表头,只返回数据
获取表头中,是header =true 这样可以获取表头数据内容
完整site代码如下
from django.contrib import admin
from django.urls import path,re_path
from django.shortcuts import render,HttpResponse
from django.utils.safestring import mark_safe
from django.urls import reverse class ModelStark(object): list_display =("__str__",) def __init__(self,model): self.model = model
self.model_name = self.model._meta.model_name
self.app_label = self.model._meta.app_label # 反向解析当前访问表的增删改查URL
def get_list_url(self):
# 反向解析当前表的查询的URL
list_url = reverse("%s_%s_list" % (self.app_label, self.model_name))
return list_url def get_add_url(self, obj):
# 反向解析当前表的添加的URL
add_url = reverse("%s_%s_delete" % (self.app_label, self.model_name))
return add_url def get_delete_url(self, obj):
# 反向解析当前表的删除的URL
delete_url = reverse("%s_%s_delete" % (self.app_label, self.model_name), args=(obj.pk,))
return delete_url def get_change_url(self, obj):
# 反向解析当前表的修改的URL
change_url = reverse("%s_%s_change" % (self.app_label, self.model_name), args=(obj.pk,))
return change_url # 三个默认列
# 选择框
def show_checkbox(self, obj=None, heade=False):
if heade:
return mark_safe("<input type='checkbox'>")
return mark_safe("<input type='checkbox'>") # 删除框 def show_delbtn(self, obj=None, heade=False):
if heade:
return '删除'
# return mark_safe("<a href='stark/app01/book/%s/delete'>删除</a>" % obj.pk)
return mark_safe("<a href='%s'>删除</a>" % self.get_delete_url(obj))
# 编辑框 def show_editbtn(self, obj=None, heade=False):
if heade:
return '编辑'
# return mark_safe("<a href='stark/app01/book/%s/change'>编辑</a>" % obj.pk)
return mark_safe("<a href='%s'>编辑</a>" % self.get_change_url(obj))
# 构建新的list_display def get_new_list_display(self):
temp = []
temp.extend(self.list_display)
temp.append(ModelStark.show_editbtn)
temp.append(ModelStark.show_delbtn)
temp.insert(0, ModelStark.show_checkbox) return temp # 视图函数
def list_view(self, request):
"""
self:当前配置类
selfmodel:当前访问的表数据
:param request:
:return:
"""
# 当前访问表的数据
querset = self.model.objects.all()
print(querset) print(self.list_display) #用于展示头部文件
header_list=[]
for field_or_info in self.get_new_list_display():
#判断是函数名或者是字符段
if callable(field_or_info):
vim=field_or_info(self,heade=True)
header_list.append(vim)
else:
# 获取指定字段的对象属性,并拿出verbose_name属性
if field_or_info=='__str__':
#如果只有默认装饰类,只有__str__,则拿出他的表名作为头
vim = self.model._meta.model_name.upper()
else:
file_obj =self.model._meta.get_field(field_or_info)
vim = file_obj.verbose_name
header_list.append(vim) #用于构建视图中的展示数据
new_data=[]
for obj in querset:
temp=[]
for field_or_info in self.get_new_list_display():
# 判断是函数还是字符段
if callable(field_or_info):
vim = field_or_info(self,obj)
else:
try:
from django.db.models.fields.related import ManyToManyField
info_obj=self.model._meta.get_field(field_or_info)
# 判断多对多字段
if type(info_obj)==ManyToManyField:
raise Exception("list_distplay 不能是多不多字段")
#判断是否是__str__
# if field_or_info=='__str':
# vim=getattr(obj,field_or_info)()
# 判断是否有choices字段
if info_obj.choices:
vim = getattr(obj,'get_%s_display'%field_or_info)()
else:
vim =getattr(obj,field_or_info)
except Exception as e:
vim = getattr(obj, field_or_info)()
temp.append(vim)
new_data.append(temp)
print('new_data',new_data) # 目标数据
# new_data=[
# ["python",123],
# ["java",234]
# ] return render(request,'stark/list_view.html',locals()) def add_view(self, request):
return HttpResponse("add_view") def change_view(self, request, id):
return HttpResponse("change_view") def delete_view(self, request, id):
return HttpResponse("delete_view") @property
def get_url(self,):
# temp = [
# path("", self.list_view),
# path("add/",self. add_view),
# re_path("(\d+)/change/", self.change_view),
# re_path("(\d+)/delete/", self.delete_view),
# ]
temp = [
path("", self.list_view, name="%s_%s_list" % (self.app_label, self.model_name)),
path("add/", self.add_view, name="%s_%s_add" % (self.app_label, self.model_name)),
re_path("(\d+)/change/", self.change_view, name="%s_%s_change" % (self.app_label, self.model_name)),
re_path("(\d+)/delete/", self.delete_view, name="%s_%s_delete" % (self.app_label, self.model_name)),
]
return (temp, None, None) class StarkSite:
def __init__(self):
self._registry ={} def register(self,model,admin_class=None,**options):
admin_class = admin_class or ModelStark
self._registry[model]=admin_class(model) def get_urls(self):
temp =[] # 拿到已经注册的所有表
for model,config_obj in self._registry.items():
# 表名
model_name = model._meta.model_name
# 项目名
model_label = model._meta.app_label
temp.append(
path("%s/%s/"%(model_label,model_name),config_obj.get_url)
)
return temp
@property
def urls(self):
return self.get_urls(),None,None site = StarkSite()
site代码
完整注册stark代码如下
from stark.services.sites import site,ModelStark
from .models import *
from django.utils.safestring import mark_safe class BookConfig(ModelStark): def show_authors(self,obj=None,heade=False):
if heade:
return "作者信息" return " ".join([author.name for author in obj.author.all()])
# # 选择框
# def show_checkbox(self,obj=None,heade=False):
# if heade:
# return mark_safe("<input type='checkbox'>")
# return mark_safe("<input type='checkbox'>")
#
# # 删除框
# def show_delbtn(self, obj=None, heade=False):
# if heade:
# return '删除'
# return mark_safe("<a href='stark/app01/book/%s/delete'>删除</a>"%obj.pk)
#
# # 编辑框
# def show_editbtn(self, obj=None, heade=False):
# if heade:
# return '编辑'
# return mark_safe("<a href='stark/app01/book/%s/change'>编辑</a>" % obj.pk) list_display=["title","price","staes","publish",show_authors]
# list_display=["title","price","staes","publish"] site.register(Book,BookConfig)
site.register(Publish)
print(site._registry)
stark代码
a
Django-CRM项目学习(二)-模仿admin实现stark的更多相关文章
- Spring Boot 项目学习 (二) MySql + MyBatis 注解 + 分页控件 配置
0 引言 本文主要在Spring Boot 基础项目的基础上,添加 Mysql .MyBatis(注解方式)与 分页控件 的配置,用于协助完成数据库操作. 1 创建数据表 这个过程就暂时省略了. 2 ...
- Django - CRM项目(3)
一.CRM项目的业务逻辑与表结构梳理 1.分析业务逻辑 (1) 引流(sem) (2) 网络咨询师(客服):添加客户信息和查看客户,分配销售 (3) 销售:查看私户 添加跟进记录 失败:加入公户 成功 ...
- Django - CRM项目(2)Q查询(模糊查询)
一.CRM项目(2) 利用Q查询中的q对象完成条件筛选功能. 批量删除.公户转私户功能. 新增一张跟进记录表ConsultRecord,迁移数据库并添加测试数据,实现跟进记录列表页面. 客户列表新增跟 ...
- WPF项目学习.二
WPF用MVVM的解决记录 版权声明:本文为博主初学经验,未经博主允许不得转载. 一.前言 记录在学习与制作WPF过程中遇到的解决方案. 焦点的控制,键盘事件触发,输入框的数字限制,异步处理,隐藏状 ...
- django——CRM项目
1.引言 CRM,客户关系管理系统(Customer Relationship Management).企业用CRM技术来管理与客户之间的关系,以求提升企业成功的管理方式,其目的是协助企业管理销售循环 ...
- Django ---- blog项目学习所得
一.登录功能 1.采用ajax 提交form表单的方式 2.后台生成随机验证码,登录时提交验证码 3.用PLI库生成随机验证码,置于session中,登录时与前台提交的code进行upeer()的验证 ...
- php开源项目学习二次开发的计划
开源项目: cms 国内 dedecms cmstop 国外 joomla, drupal 电商 国内 ecshop 国外 Magento 论坛 discuz 博客 wordpress 学习时 ...
- django 基础框架学习 (二)
Django框架基础-02 Django缓存cookie 1.说明 当我们服务器在响应数据的同时,希望写⼊⼀些缓存数据到客户端 我们可以选择在响应的同时,将要写⼊到客户端的 ...
- android 开源项目学习<二>
roottools: RootTools gives Rooted developers easy access to common rooted tools... https://code.g ...
随机推荐
- Chapter 5 Blood Type——3
Disappointment flooded through me as my eyes unerringly focused on his table. 当我的眼睛完全集中在他的桌上时,失望如洪水般 ...
- log4j2.yml配置文件
# https://blog.csdn.net/u010598111/article/details/80556437 # 共有8个级别,按照从低到高为:ALL < TRACE < DEB ...
- Magicodes.WeiChat——V3.0(多租户)版本发布
主要内容如下: 添加项目Magicodes.WeiChat.Data.Multitenant,全面支持多租户(基于EF已经ASP.NET Identity) 增加租户管理.租户成员管理.修改密码.公众 ...
- 一统江湖的大前端(3) DOClever——你的postman有点low
<一统江湖的大前端>系列是自己的前端学习笔记,旨在介绍javascript在非网页开发领域的应用案例和发现各类好玩的js库,不定期更新.如果你对前端的理解还是写写页面绑绑事件,那你真的是有 ...
- Atom编辑器插件
一.atom由于安装的插件过多导致tab键失效解决办法如下: 打开File→Keymap中keymap.cson文件,将以下代码复制到文件: 'atom-text-editor:not([mini]) ...
- Powerdesigner逆向工程64位Oracle数据库
Powerdesigner老版本不支持64位Client,新版本弄不到破解码 解决方法,用Powerdesigner+32位Oracle Clent访问64位Oracle Server 遇到的坑分享下 ...
- 初识 MongoDB,MongoDB 的安装运行
1. MongoDB 非关系型数据库 MongoDB是一个基于分布式文件存储的数据库,由C++语言编写.目的是为WEB应用提供扩展的高性能的数据存储解决方案.MongoDB是一个介于关系型数据库和 ...
- Mybaits之Mapper动态代理开发
Mybaits之Mapper动态代理开发 开发规范: Mapper接口开发方法只需要程序员与Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法 ...
- PHP 中的Trait
概述 在PHP中有一种代码复用的技术, 因为单继承的问题, 有些公共方法无法在父类中写出, 而 Trait可以应对这种情况, 它可以定义一些复用的方法, 然后在你需要使用的类中将其引入即可. 刚开始的 ...
- Djiango初识
加载静态文件 在一个网页中,不仅仅只有一个 html 骨架,还需要 css 样式文件, js 执行文件以及一些图片 等.因此在 DTL 中加载静态文件是一个必须要解决的问题.在 DTL 中,使用 st ...