Python入门自学进阶-Web框架——26、DjangoAdmin项目应用-数据记录操作
创始人
2024-01-30 13:54:02
0

对于每个表显示的数据,点击其中一条,进入这条数据的修改页面,显示此条数据的具体内容,并提供修改、删除等功能。主要是ModelForm的应用。

一、记录数据修改

首先是路由项的添加,点击一条记录后,进入相应的记录显示修改页面:

urls.py中增加路由项:path('///change/',views.rec_obj_change,name='rec_change'),

视图函数中增加对应的函数rec_obj_change:

def rec_obj_change(req,app_name,table_name,id_num):print(app_name,table_name,id_num)return render(req,'mytestapp/rec_change.html')

模板中增加rec_change.html:

{% extends 'base.html' %}
{% load tags %}
{% block mybody %}
数据表:
数据项111
{% endblock %}

在前一步骤显示数据的基础上,将数据的第一列增加上a标签,最终形成///change/格式的href,跳转到rec_change.html页面。

首先改造数据显示列表,将第一列做成a标签:

@register.simple_tag
def build_table_row(obj,admin_class,url_path):row_ele = ""print("===///:::",obj)for row_data in obj:row_ele = row_ele +""for index_ele,column in enumerate(admin_class.list_display):field_obj = row_data._meta.get_field(column)if field_obj.choices:column_data = getattr(row_data,"get_%s_display"%column)()else:column_data = getattr(row_data,column)field_obj1 = getattr(row_data,column)if hasattr(field_obj1,'values'):s = ""dic1 = field_obj1.values()[0]print(type(dic1),dic1)for v in dic1.values():s = s + str(v) + ';'column_data = sif index_ele == 0:   # 若果是第一列,则加上a标签,可以跳转到修改页row_ele += '%s'%(url_path,row_data.id,column_data)else:row_ele += "%s"%column_datarow_ele = row_ele + ""print(row_ele)return mark_safe(row_ele)

在遍历admin_class中的list_display时,使用enumerate,生成index及数据,对于index为0的,即显示数据的第一列加上a标签,a标签的href为mytestapp/customer/3/change/格式,其中3是id的值。这样,点击对应的数据的第一列,就进入rec_change.html页。

rec_change.html页对每条记录的数据进行详细显示,并提供修改、验证功能。

修改、验证用到前面学过的ModelForm类,自动生成前端标签,提供基本的验证功能。

温习一下ModelForm:

class CustomerModelForm(forms.ModelForm):class Meta:model = models.Customerfields = "__all__"
def rec_obj_change(req,app_name,table_name,id_num):print(app_name,table_name,id_num)obj = CustomerModelForm()return render(req,'mytestapp/rec_change.html',{'obj':obj})

前端将“数据项111”改成{{ obj }},将显示如下

 先定义CustomerModelForm类,在视图函数中生成这个类的实例,返回给前端,前端就能自动生成对应的标签,并具有基本的验证功能。ModelForm类主要是指定Meta中的model以及fields。

因为我们是针对所有的Model类,即所有的表都会进行数据修改,不能定义一个Model,就定义一个ModelForm,这里要动态生成ModelForm。

def create_model_form(req,admin_class):# 动态生成ModelForm类,主要使用type函数class Meta:model = admin_class.modelfields = "__all__"model_form_class = type("DynamicModelForm",(ModelForm,),{'Meta':Meta})# setattr(model_form_class,'Meta',Meta)  # 以这种方式给动态生成的类加Meta不好用return model_form_class
def rec_obj_change(req,app_name,table_name,id_num):admin_class = mytestapp_admin.enable_admins[app_name][table_name]model_form_class = myutils.create_model_form(req,admin_class)obj = admin_class.model.objects.get(id = id_num)form_obj = model_form_class(instance=obj)return render(req,'mytestapp/rec_change.html',{'obj':form_obj})

前端:

数据表:
{{ obj.as_ul }}

{{ obj.as_ul }}显示:

 美化一下:

{% for f in obj %}
{{ f }}
{% endfor %}

将上述字段包入form标签中,并增加修改保存按钮:

{% csrf_token %}
{% for f in obj %}{% if f.field.required %} {% else %}{% endif %}
{{ f }}
{% endfor %}

 使用post方法提交到本页面,后端进行修改保存

 后端修改:

def rec_obj_change(req,app_name,table_name,id_num):admin_class = mytestapp_admin.enable_admins[app_name][table_name]model_form_class = myutils.create_model_form(req,admin_class)obj = admin_class.model.objects.get(id=id_num)if req.method == "POST":form_obj = model_form_class(req.POST,instance=obj)# ModelForm参数为一个时,是新建一条记录,当有两个参数时,就是修改# POST方法就是进行记录修改的,所以需要两个参数,第二个是instance=参数if form_obj.is_valid():form_obj.save()else:    # 这是GET请求,所以是新建一个ModelForm,进行显示form_obj = model_form_class(instance=obj)return render(req,'mytestapp/rec_change.html',{'obj':form_obj})

在生成ModelForm时,就对相关字段添加前端生成标签时的样式进行设置,即进行class属性设置:

def create_model_form(req,admin_class):# 动态生成ModelForm类,主要使用type函数def __new__(cls, *args, **kwargs):# 対生成ModelForm中的字段添加前端样式,即在前端自动生成标签时带上class属性cls.base_fields['qq'].widget.attrs['class'] = 'form-control'return ModelForm.__new__(cls)class Meta:model = admin_class.modelfields = "__all__"model_form_class = type("DynamicModelForm",(ModelForm,),{'Meta':Meta})setattr(model_form_class,'__new__',__new__)  # 以这种方式给动态生成的类加__new__方法return model_form_class

base_fields是所有字段的列表。

二、记录数据增加

修改数据显示前端文件,增加一个Add标签:

表数据管理: {{ model_class_name }}

增加路由项:

path('//add/',views.rec_obj_add,name='rec_add'),

增加视图函数rec_obj_add:

def rec_obj_add(req,app_name,table_name):admin_class = mytestapp_admin.enable_admins[app_name][table_name]model_form_class = myutils.create_model_form(req, admin_class)if req.method == "POST":form_obj = model_form_class(req.POST)# ModelForm参数为一个时,是新建一条记录,POST方法提交,是新建一条记录if form_obj.is_valid():form_obj.save()return redirect(req.path.replace("/add/","/")else:    # 这是GET请求,所以是新建一个空ModelFormform_obj = model_form_class()return render(req,"mytestapp/rec_add.html",{'obj':form_obj,'model_name':admin_class.model.__name__})

前端显示页面rec_add.html:

{% extends 'base.html' %}
{% load tags %}
{% block mybody %}
数据表:{{ model_name }} ————数据添加
{{ obj.errors }}
{% csrf_token %}
{% for f in obj %}{% if f.field.required %}{% else %}{% endif %}
{{ f }}
{% endfor %}
{% endblock %}

 

三、记录数据修改、增加中多选下拉框不同样式设计

在Django的admin应用中,如果配置了filter_horizontal = ['tags'],则显示如下的样式:

 自己实现类似的功能。

首先在admin_class类中增加配置filter_horizontal = ['tags']

在前端显示表字段时,要进行判断,即如果字段值在filter_horizontal中,就要使用上述的格式显示。

首先要写两个标签,来获取多选下拉框中没有被选中的项和已经选中的项的数据集:

@register.simple_tag
def get_m2m_obj_list(admin_class,f):  # 获取没有被选择的所有项field_obj = getattr(admin_class.model,f.name)all_obj_list = field_obj.rel.model.objects.all()selected_list = f.initialprint('================================all=======')print(all_obj_list)print('=========================selected=======')print(selected_list)noselect_list = []for obj in all_obj_list:if obj not in selected_list:noselect_list.append(obj)return noselect_list@register.simple_tag
def get_m2m_selected_list(form_obj,f):    # 获取所有已选择的项selected_obj = getattr(form_obj.instance,f.name)print(selected_obj.all())return selected_obj.all()

前端循环,填充进入不同的下拉框:

{% csrf_token %}
{% for f in form_obj %}{% if f.field.required %}{% else %}{% endif %}
{% if f.name in admin_class.filter_horizontal %}
{% get_m2m_obj_list admin_class f as m2m_obj_list %}
===》
《===
{% else %}{{ f }}{% endif %}
{% endfor %}

关键点是如何将对应记录的多选下拉框填充上正确的数据,model类的字段的rel.model属性指向字段关联的外键表对应的model,通过这个model,即可查询出下拉框的所有数据,在此基础上,找出未选择和已选择数据。ModelForm每个字段的initial属性,也指出了已选择的数据,可以直接在前端使用。最终样式:

数据填上后,就是要前端实现双击数据实现数据在两个多选下拉框之间移动,这主要是前端技术,与python就无关了:

{% csrf_token %}
{% for f in form_obj %}{% if f.field.required %}{% else %}{% endif %}
{% if f.name in admin_class.filter_horizontal %}
{% get_m2m_obj_list admin_class f as m2m_obj_list %}
===》
《===
{% else %}{{ f }}{% endif %}
{% endfor %}

然后在提交的时候,需要再对应选择的多选下拉框中的数据做全部选中状态,这样提交时才能带过去数据:

,增加onsubmit

        function SelectedAll() {$("select[my_id='selectedalloption'] option").each(function () {$(this).prop("selected",true)});return true}

 有一个BUG,在前端使用f.initial遍历已选数据项时,提交后数据没有刷新,修改使用自定义标签

{% csrf_token %}
{% for f in form_obj %}{% if f.field.required %}{% else %}{% endif %}
{% if f.name in admin_class.filter_horizontal %}
{% get_m2m_obj_list admin_class f form_obj as m2m_obj_list %}
===》
《===
{% else %}{{ f }}{% endif %}
{% endfor %}
@register.simple_tag
def get_m2m_obj_list(admin_class,f,form_obj):  # 获取没有被选择的所有项field_obj = getattr(admin_class.model,f.name)all_obj_list = field_obj.rel.model.objects.all()if field_obj.instance.id:selected_list = getattr(form_obj.instance,f.name).all()else: # 对于新增记录,没有id,直接返回所有return all_obj_listprint('================================all=======')print(all_obj_list)print('=========================selected=======')print(selected_list)noselect_list = []for obj in all_obj_list:if obj not in selected_list:noselect_list.append(obj)print('=====没有选择的====',noselect_list)return noselect_list@register.simple_tag
def get_m2m_selected_list(form_obj,f):    # 获取所有已选择的项selected_obj = getattr(form_obj.instance,f.name)print('标签,已选:',selected_obj.all())return selected_obj.all()

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...