废话少说,接下来开始进入正题!!!
Django的项目新建这里就不说了,此处省略100字.......
通过看django的源码可以得知:Django启动时,自动加载settings配置文件中的installed_apps,然后执行源码中的autodiscover()方法来顺序加载apps对应的admin.py文件。所以我们可以通过修改apps中的代码让django来执行我们写的stark.py文件而不执行admin.py文件.
from django.apps import AppConfig
from django.utils.module_loading import autodiscover_modules
class StarkConfig(AppConfig):
name = 'stark'
# 这个ready方法是固定不变的
def ready(self):
autodiscover_modules('stark')
先在app名为stark的文件夹下新建一个servers包,在servers下新建一个site.py文件,在site.py文件中写如下代码:
class StarkSite(object):
def __init__(self):
#定义一个字典用于存储接下来需要注册的model和对应congfig配置类
self._registry = {}
def register(self, model, admin_class=None):
# 设置配置类,有自定义的就用自定义的,没有就用默认的ModelStark
if not admin_class:
admin_class = ModelStark
#以model为键,配置类实例化对象为值进行注册
self._registry[model] = admin_class(model)
site = StarkSite()
执行每个app下的stark.py文件来注册所有app下用户提交的需要注册的model表和对应的config配置类
//导入默认配置类ModelStark,(这个类稍后再创建)
from stark.servers.site import ModelStark
//site是StarkSite类的实例化对象,通过模块导入实现的单利模式
from stark.servers.site import site
//导入app01下的models文件
from app01 import models
//注册models表
site.register(models.Book)
site.register(models.Publish)
site.register(models.Author)
site.register(models.AuthorDetail)
from django.db import models
class Author(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32,verbose_name='作者')
age = models.IntegerField(verbose_name="年龄")
# 与AuthorDetail建立一对一的关系
authorDetail = models.OneToOneField(to="AuthorDetail", on_delete=models.CASCADE)
def __str__(self):
return self.name
class AuthorDetail(models.Model):
nid = models.AutoField(primary_key=True)
birthday = models.DateField()
telephone = models.BigIntegerField()
addr = models.CharField(max_length=64)
class Publish(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32,verbose_name='出版社')
city = models.CharField(max_length=32,verbose_name="城市")
email = models.EmailField(verbose_name='邮箱')
def __str__(self):
return self.name
class Book(models.Model):
nid = models.AutoField(primary_key=True)
title = models.CharField(max_length=32,verbose_name='书名')
price = models.DecimalField(max_digits=5, decimal_places=2,verbose_name="价格")
# 与Publish建立一对多的关系,外键字段建立在多的一方
publish = models.ForeignKey(to="Publish", to_field="nid", on_delete=models.CASCADE,verbose_name='出版社')
publishDate = models.DateField(verbose_name="出版日期",auto_created=True)
# 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表
authors = models.ManyToManyField(to='Author',verbose_name='作者' )
def __str__(self):
return self.title
url的设计中,我们采用和django-admin相同的一下这种方式:
url('正则表达式',([ url列表 ],None,None)) 规则
from django.conf.urls import url
from stark.servers.site import site
urlpatterns = [
url(r"^stark/",site.urls)
]
class StarkSite(object):
def __init__(self):
#定义一个字典用于存储接下来需要注册的model和对应congfig配置类
self._registry = {}
def register(self, model, stark_class=None):
# 设置配置类,有自定义的就用自定义的,没有就用默认的ModelStark
if not stark_class:
stark_class = ModelStark
#以model为键,配置类实例化对象为值进行注册
self._registry[model] = stark_class(model)
def get_urls(self):
temp = []
# self._registry是以model为键,config_obj配置类实例化对象为值进行注册后的字典容器
for model, config_obj in self._registry.items():
# 通过模型表model的._meta方法得到动态的、注册的、model的名字和model所属的app名
model_name = model._meta.model_name
app_label = model._meta.app_label
# "%s/%s/" % (app_label, model_name)字符串拼接先得到所需路径的前边部分'app/model/'
# config_obj.urls为通过配置类对象调用配置类下的urls方法来得到相应model表的增删改查url
#--->用到 了路由的二级分发
temp.append(url(r"%s/%s/" % (app_label, model_name), config_obj.urls))
return temp
@property
def urls(self):
return self.get_urls(), None, None
class ModelStark(object):
def get_urls(self):
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label
temp = [
#给每条增删改查的url设置不同的name,后续需要通过反向解析找到对应的url
url(r"^$", self.listview, name=f'{app_label}_{model_name}_check'),
url(r"add/$", self.addview, name=f'{app_label}_{model_name}_add'),
url(r"(\d+)/change/$", self.changeview, name=f'{app_label}_{model_name}_change'),
url(r"(\d+)/delete/$", self.delview, name=f'{app_label}_{model_name}_delete'),
]
return temp
@property
def urls(self):
return self.get_urls(), None, None
代码执行到这里就为注册的每张表生成了增、删、改、查四个url
以上代码段的执行顺序图示:
接下来的所有逻辑将基于site.py下的ModelStark配置类完成:
注意:由于在StarkSite类中代码的第一步就是判断用户有没有自己stark_class配置类,所以通过这种方式来扩展出来的用户的自定义功能,自己的配置类中有相应的方法或者属性就用自己的,没有就用默认的配置类中的。这都是基于用户在自定义配置类时主动继承默认配置类ModelStark。这个项目充分利用了python的面向对象类的继承、调用、封装等知识点。
在ModelStark类中有两个贯穿全局的参数:谨记谨记
self:当前model模型表的配置类对象
self.model:当前model模型表
通过配置类对象中的new_list_display列表得到表头的展示字段或者默认显示的操作名称,将操作的具体逻辑封装成函数来调用,这其中用到了一下知识点:
def create_head(self):
# 表头的建立
heads_list = []
for field_or_func in self.config_obj.new_list_display():
if callable(field_or_func):
head = field_or_func(self.config_obj, is_head=True)
else:
if field_or_func != "__str__":
field_obj = self.config_obj.model._meta.get_field(field_or_func)
head = field_obj.verbose_name
else:
head = self.config_obj.model._meta.model_name
heads_list.append(head)
return heads_list
这部分的逻辑和表头的设计很像,不同的有以下几点:
用到一下知识点:
def create_body(self):
# 表内容content_list=[[数据1],[数据2],[数据3]....]
obj_list = []
for data_obj in self.data_list:
content_list = []
for field_or_func in self.config_obj.new_list_display():
if callable(field_or_func):
content = field_or_func(self.config_obj, data_obj)
else:
try:
from django.db.models.fields.related import ManyToManyField
field_obj = data_obj._meta.get_field(field_or_func)
if isinstance(field_obj, ManyToManyField):
contents_list = getattr(data_obj, field_or_func).all()
content = '||'.join([str(item) for item in contents_list])
else:
content = getattr(data_obj, field_or_func)
if field_or_func in self.config_obj.list_display_links:
url = self.config_obj.get_change_url(data_obj)
content = mark_safe(f'{content}')
except:
content = getattr(data_obj, field_or_func)
content_list.append(content)
obj_list.append(content_list)
return obj_list
用到的知识点:
通过配置类对象中的search_fields列表得到相应的查询字段
用到的知识点:
from django.db.models import Q
//实例化创建Q对象
search_condition = Q()
//对象.connector可以修改Q的查询条件,默认的是and条件
search_condition.connector = "or"
//通过.children.append向Q()对象中添加元组类型('field',condition)的筛选条件
search_condition.children.append((f"{field}__icontains", condition))
//过滤数据
ret = queryset.filter(search_conditions)
用到的知识点:
def actions_list(self):
actions_list = []
action_info = []
if self.config_obj.actions:
actions_list.extend(self.config_obj.actions)
actions_list.append(self.config_obj.batch_delete)
for action in actions_list:
action_info.append({
"desc": action.short_description,
"action": action.__name__
})
return action_info //处理得到前端所需要的数据
//视图处理函数,处理用户发过来的请求,执行相应的方法操作
if request.method == "POST":
pk_list = request.POST.getlist('checkbox_pk')
queryset = self.model.objects.filter(pk__in=pk_list)
action_name = request.POST.get("action")
try:
action = getattr(self, action_name)
action(queryset)
except:
code = 1
filter过滤主要处理的是model表中的一对多和多对多的字段,通过filter来筛选出数据的交集,filter的设计主要分为三个方面:
用到的知识点:
//url设计代码段
def filter_field_links(self):
filter_links = {}
for field in self.config_obj.list_filter:
# 从get请求中得到需要的filter参数
params = copy.deepcopy(self.request.GET)
choice_field_pk = self.request.GET.get(field, 0)
# 通过多对多或者一对多的字段得到对应的模型表
field_obj = self.config_obj.model._meta.get_field(field)
rel_model = field_obj.rel.to
# 得到模型表中的所有数据
ret_model_queryset = rel_model.objects.all()
tem = []
for obj in ret_model_queryset:
# 将对应的对象pk值添加到字典中,以当前model表的字段为键
params[field] = obj.pk
if obj.pk == int(choice_field_pk):
link = f"{obj}"
else:
link = f"{obj}"
tem.append(link)
filter_links[field] = tem
return filter_links
在model字段中经常存在着表与表的关联,所以在创建新数据或者修改数据时需要同时创建相关联的数据,
业务逻辑:
执行思路:
就先到这吧!!!