django学习

【【Django】】

【【多表查询】】

select_related() 使用innerjoin预先进行多表查询,会默认递归查询模型所有一对一和多对一外键模型。只需要进行某一个字段的innerjoin,可以通过参数指定。
一般情况可以适用所有正向查询。
反向查询中,一找一适用,一找多无法适用:
Contract.object.select_related(receipt_set) 报错!
*fields 参数
该参数填写外键字段名即可,如果要深入到外键的外键,按照django常理进行__查找即可。例子:
Contract.object.select_related(prgsheet) 只填充进度表,可以使用,填充多个。
Contract.object.select_related(prgsheet__equipment) 填充进度表和人员配备
depth 参数
也可以通过depth 参数指定递归的层次。
Contract.object.select_related(depth = 1)将会填充prgsheet和equipment
select_related是通过join来关联多表,一次获取数据,存放在内存中,但如果关联的表太多,会严重影响数据库性能。

prefetch_related()
对于多对多字段(ManyToManyField)和一对多字段,可以使用prefetch_related()来进行优化
它专门用来处理1找多问题,无论是借助中间表实现的多对多,还是反向一对多,都可以用它来实现。
实际上,prefetch_related()也可以处理正向查询,其运作流程如下:

如果查询为正向多找一查询,会默认使用类似select_related的join查询,连表查处结果。
如果查询为一找多的反向查询,如Contract.object.prefetch_related(receipt_set),第一步会查询符合条件的contract,然后使用in 查询所有contract_id in 找到的contract列表中的receipt,最后将从属于各个contract的receipt预填充到他们的receipt_set relation中。

需要注意的是,填充到receipt_set.all()中的实际上是一个list,它不可以使用queryset的功能,只能遍历;一旦调用任何queryset功能,它将重新查询并进行填充。

select_related()的效率要高于prefetch_related()。因此,最好在能用select_related()的地方尽量使用它,也就是说,对于ForeignKey字段,避免使用prefetch_related()。

两个函数可以联用:
plist = Order.objects.select_related(‘customer’).prefetch_related(‘customer__visitation__province’)
Django会先做select_related,之后prefetch_related的时候会利用之前缓存的数据,从而避免了1次额外的SQL查询

【【聚合查询】】
合成单一数据,返回的是一个字典
models.Book.objects.all().aggregate(Avg(“price”))
{‘price__avg’: 192.593333}
models.Book.objects.all().aggregate(avg_price=Avg(“price”))
{‘avg_price’: 192.593333}
也可以输入多个聚合参数:返回一个多参数字典
models.Book.objects.all().aggregate(Avg(“price”),Max(“price”),Min(“price”),Sum(“price”))
{‘price__avg’: 192.593333, ‘price__max’: Decimal(‘233.33’), ‘price__min’: Decimal(‘111.12’), ‘price__sum’: Decimal(‘577.78’)}

分组查询:
使用.annotate,会自动根据聚合的字段进行分组,然后为每个对象生成一个独立的组内聚合统计值。
通常选择聚合外键,这样可以得到外键的最大最小求和等信息。

统计每一本书的作者个数
book_list = models.Book.objects.all().annotate(authorNum=Count(“author”))
print(book.authorNum)

统计出每个出版社买的最便宜的书的价格
pulisher_list = models.Publisher.objects.all().annotate(min_price=Min(“book__price”))
print(i.min_price)

统计不止一个作者的图书
tmp = models.Book.objects.all().annotate(au=Count(“author”)).filter(au__gt=1)

根据一本图书作者数量的多少对查询集 QuerySet进行排序
tmp = models.Book.objects.all().annotate(au=Count(“author”)).order_by(“au”)

查询各个作者出的书的总价格
tmp = models.Author.objects.all().annotate(total=Sum(“books__price”)).values(“name”,“total”)

注意:此时annotate默认以ID分组(打印query可以看出),所以可以聚合所有外键字段,但如果聚合本身的字段,就达不到目的了。
注意2:如果想自定义group by 字段,可以在annotate之前使用values,如:
PxbNCEUserQuest.objects.filter(user_id=335).values(“user_id”).annotate(qid=Min(“question_id”), cid=Min(“catalog_id”)).values(“user_id”, “qid”, “cid”)
第一个values用来选中需要用来group by的字段(此处group by user_id)
第二个values用来指定实际select的字段
注意3:如果有order_by存在,默认以该字段进行group_by,包括使用Meta里的ordering,所以最好在结尾使用一个空排序:order_by()

【常用命令】
django-admin startproject mysite 建立目录
python manage.py runserver 开启服务器
python manage.py startapp [appname] 建立app项目
python manage.py shell 进入调试模式
python manage.py makemigrations [appname] 搜索修改,生成建表语句
python manage.py migrate 进行数据库建表命令
python manage.py createsuperuser 创建超级管理员
python manage.py test appname 测试

【路由】
from django.urls import include, path
app_name = ‘polls’#命名空间
urlpatterns = [
path(‘polls/’, include(‘polls.urls’),name=’’),#表示扣除polls后转发给下一级路由,可以取名,在模版HTML文件中使用url标签进行搜索。如同同名,比如每个app都有index,应该使用命名空间区分。
path(‘admin/’, admin.site.urls),
]

【视图函数】
对应MVC的C层(V是模版),作用是在进入真实显示内容的HTML前取出数据,验证数据。
def detail(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, ‘polls/detail.html’, {‘question’: question})
注意,模版存放在各个app的templates文件夹中,render的根目录就是各个templates文件夹,因此很容易重名(例如都有两个detail.html文件,此时最好再建一层子文件夹,以appname/detail.html进行定位)

类视图】
class DetailView(generic.DetailView):
model = Question
template_name = ‘polls/detail.html’
ontext_object_name=‘q’

class IndexView(generic.ListView):
template_name = ‘polls/index.html’
context_object_name = ‘latest_question_list’
类视图在路由应该这样写:path(‘admin/’, view.detailview.as_view),调用类的内置方法。

【模版】
模版HTML文件中如果要超链接到别的视图函数进行处理,应该使用{% url %}
href="{% url ‘polls:detail’ question.id %}" 这里是自带了命名空间的。
静态文件】
和模版一样,需要在app文件夹下建立static文件夹,为防止文件重名,也需要建立子目录层。不同的是,主目录如果要建立静态文件static,需要在设置加上:STATIC_URL = ‘/static/’ STATICFILES_DIRS = [os.path.join(BASE_DIR, ‘static’)]
而在模版HTML文件中访问静态资源,需要使用static标签:
{% load static %}
<link rel=“stylesheet” type=“text/css” href="{% static ‘polls/style.css’ %}">
在静态资源中访问别的静态资源,除了使用相对路径,可以使用url函数:
url(“images/background.gif”)

【【表单和crispy_forms】】

class ExampleForm(forms.Form):
like_website = forms.TypedChoiceField(
label = “Do you like this website?”,
choices = ((1, “Yes”), (0, “No”)),
coerce = lambda x: bool(int(x)),
widget = forms.RadioSelect,
initial = ‘1’,
required = True,
)

favorite_food = forms.CharField(
    label = "What is your favorite food?",
    max_length = 80,
    required = True,
)

基本使用方法和models.model差不多,字段类型也基本和models有定义,以下介绍常用几种:
CharField 输入框,输入文本
IntegerField/DecimalField/FloatField 输入框,限制数字
ChoiceField/TypedChoiceField/TypedMultipleChoiceField select框
DateField/TimeField/DateTimeField 时间选择
DurationField 时间间隔
RegexField 使用正则表达式作为掩码限制的输入框
EmailField 只能输入email的输入框
ImageField/FileField 图片、文件输入框
ModelChoiceField/ModelMultipleChoiceField 需要直接选择系统model的特殊转化select

feild的参数:
required=True, 是否允许为空
widget=None, HTML插件
label=None, 用于生成Label标签或显示内容
initial=None, 初始值
help_text=’’, 帮助信息(在标签旁边显示)
error_messages=None, 错误信息 {‘required’: ‘不能为空’, ‘invalid’: ‘格式错误’}
validators=[], 自定义验证规则
localize=False, 是否支持本地化
disabled=False, 是否可以编辑
label_suffix=None Label内容后缀

helper】
from crispy_forms.helper import FormHelper
helper = FormHelper()
helper.form_id = ‘id-exampleForm’ 设置form的id
helper.form_class = ‘blueForms’ 设置form的class
helper.form_method = ‘post’ 设置method
helper.form_action = ‘submit_survey’ 设置action
helper.add_input(Submit(‘submit’, ‘Submit’)) 设置提交按钮
form_obj.helper = helper
{% crispy form %}

其他属性:
.form_tag = False 将不在渲染form标签,需要自己手动写标签
.attrs={‘id’: ‘form-id’, ‘data_id’: ‘/whatever’} 字典,form上的其他属性
.disable_csrf = False 为true时将不使用crsf标签
.form_error_title non_field_error的div容器标题
.render_hidden_fields = False 是否渲染隐藏字段
.include_media = True Form Media:用来渲染表单的CSS和JavaScript资源
.help_text_inline = False 帮助文字显示在左边而非换行显示
.error_text_inline = True
.html5_required = False 为ture是显示html5required而非required
.form_show_labels = True
.label_class = ‘’ 为每一个label添加类名
.field_class = ‘’

水平表单:
.helper.form_class = 'form-horizontal 将表单渲染成水平表单
.helper.label_class = ‘col-lg-2’
.helper.field_class = ‘col-lg-8’ 各字段将添加布局的class

行内表单:
helper.form_class = ‘form-inline’
helper.field_template = ‘bootstrap3/layout/inline_field.html’
.layout = Layout(
‘email’,
‘password’,
InlineField(‘email’, readonly=True),
StrictButton(‘Sign in’, css_class=‘btn-default’),
)

【layout】
概述:
crispy 通过{% crispy form%} 标签渲染表单时,实际上调用了helper.layout的render方法。
而该方法调用get_rendered_fields方法,将layout的fields循环使用render_field方法,最终得到每个field的html,也就是说,最终负责渲染字段的方法是render_field方法,该方法渲染时,如果遇到layout_object,调用其自己的render方法,如果遇到字符串,将其包裹成feild类型渲染。
下面介绍几种layout类型:
Field】 直接继承layoutobject 终端类型,尽量不要多包裹,直接将form-group输出。
继承它的几个类:PrependedAppendedText、AppendedText、PrependedText、InlineCheckboxes、InlineRadios、UneditableField
以上几个类都使用的是super的render方法,最终调用lo的render_field方法渲染,将cs_class/wrap_class/atttr等option设置渲染出去。
xadmin中继承它的几个类:
ShowField detail使用显示只读字段的field,重写了render方法,不使用父类的render_field方法渲染,而是自己定义了模版,field只能提供字符串不能提供lo类,初始化第一个参数需要提供一个能将fieldname转化为ResultField(obj, field_name, detailadmin) callback方法,该方法已由detail中的get_field_result提供,渲染时,form字段会渲染出id,非form的read渲染出id。
ReadOnlyField eidt使用的字段字段,eidt使用wrap方法将layout中的所有readonly字段渲染成ReadOnlyField类,该类同样覆盖了render方法,自己定义了渲染模版,实际上它使用的模版其实就是上面showfield的"xadmin/layout/field_value.html"模版,传入{‘field’: field, ‘result’: result}两个字段渲染即可。该类初始化第一个参数需要提供一个detailadmin,调用其get_field_result方法获得rerultobj。该类默认传入字段名作为id,所以所有只读字段也会渲染出id。
InputGroup等见下文的xadmin layout,需要注意的是,这些字段都已经是终端字段,虽然原始的field类可以传入包裹参数或多个字符串,但使用这些类型时,最好只传入一个字符串参数。

Div】 直接继承layoutobject 包裹类型,*args就是需要包裹的多个字段,字段也可以传入包裹类型。
div渲染时,也重写了render方法,外面包裹的div层会自行渲染,渲染参数css_class、atttr、css_id等kwargs加载在div上,而它自己的feilds则调用传统的render_field渲染,所以显然,feilds可以是一个或多个包裹类型。
继承它的几个类:
Row css_class为row的特殊div
Column css_class为formColumn
xadmin中继承它的类:
Container css_class为"form-container row clearfix"
Row css_class’form-inline form-group’,会将自己的feilds中字符串全部转化为layout.Field类型,然后在上面修饰wrapper_class = col-sm-x。form-inline下的各个form-group会按行内排列,而列规则在input下基本成功,但select和redio等其他控件有的没有wrapper_class,有的修饰在其他地方,所有可能会失败。显然,row可以接受多字段,而且可以接收其他包裹类,但建议只传入字符串或field类。
Col 继承自Column,自定义分列类型,css_class = [‘column’, ‘form-column’, id, ‘col col-sm-%d’ % kwargs.get(‘span’, 6) ,可以传入多个任意包裹类型作为字段。
Main 继承自Column,column form-column main col col-sm-9 form-horizontal
Side 继承自Column,“column form-column sidebar col col-sm-3”

HTML】 可以用来渲染单独的html代码如HTML("{% if success %} <p>Operation was successful</p> {% endif %}"),它相当于一个独立的模版,可以在其中使用变量、标签、过滤器,但自定义过滤器需要自行load一遍才能使用。

几种input】:Submit(name, value)/Hidden(‘name’, ‘value’)/Button(‘name’, ‘value’)/reset = Reset(‘name’, ‘value’)都渲染出一个input标签,表现的type就是对应的类型。

以下是单独在bootsrap中能够使用的类型】:
FormActions(
Submit(‘save’, ‘Save changes’),
Button(‘cancel’, ‘Cancel’)
)
渲染出一个formaction标签的wrap,使得按钮能够匹配表单的格式。

AppendedText(‘field_name’, ‘appended text to show’)
PrependedText(‘field_name’, ‘<b>Prepended text</b> to show’)
PrependedAppendedText(‘field_name’, ‘$’, ‘.00’), 三种都渲染出一个inputgroup控件组

InlineCheckboxes(‘field_name’)
InlineRadios(‘field_name’) 一行的多选、单选组
StrictButton(text,**kwargs) 渲染出button标签,注意和上面的Button区分。
FieldWithButtons(‘field_name’, StrictButton(“Go!”)) 代按钮的字段,注意只能input,select和radio之类肯定达不到效果。
TabHolder(
Tab(‘First Tab’,
‘field_name_1’,
Div(‘field_name_2’)
),
Tab(‘Second Tab’,
Field(‘field_name_3’, css_class=“extra”)
)
)

Accordion(
AccordionGroup(‘First Group’,
‘radio_buttons’
),
AccordionGroup(‘Second Group’,
Field(‘field_name_3’, css_class=“extra”)
)
)

【对layout的操作】:
主要有wrap和wrap_together两种,而且都不是layout的方法,而是LayoutSlice的方法,要想获得一个LayoutSlice对象,可以使用以下几种方法:
helper[1:3] 切片,注意只能切片第一层layout
helper[‘password’] 使用字段名查找
helper.filter(basestring, Div) 使用类型过滤
helper.filter_by_widget(forms.PasswordInput) 使用widget过滤
.exclude_by_widget(forms.PasswordInput) 使用widget排除

方法有:
.wrap(Field, css_class=“hello”) 将字段包裹,包裹类型如果需要参数,必须在该函数添加。
.wrap_together(Field, css_class=“hello”) 将字段包裹在一个外层容器里。
.update_attributes(css_class=“hello”) 更新attr

如果需要操作layout,直接layout.fields[0]/append/extends/remove即可,layout.fields相当于一个list。

【后台admin】
python manage.py createsuperuser 创建管理员账户
注册模型类:
from django.contrib import admin
from .models import Question
admin.site.register(Question)

自定义字段显示顺序:
class QuestionAdmin(admin.ModelAdmin):
fields = [‘pub_date’, ‘question_text’]
admin.site.register(Question, QuestionAdmin)

自定义字段模块:
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
(None, {‘fields’: [‘question_text’]}),
(‘Date information’, {‘fields’: [‘pub_date’]}),
]
admin.site.register(Question, QuestionAdmin)

在新增页面把关联模型一起新增:
class ChoiceInline(admin.StackedInline):
model = Choice
extra = 3 #默认同时出现三个关联投票项
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
(None, {‘fields’: [‘question_text’]}),
(‘Date information’, {‘fields’: [‘pub_date’], ‘classes’: [‘collapse’]}),
]
inlines = [ChoiceInline]
admin.site.register(Question, QuestionAdmin)
注意:使用admin.TabularInline将显示行内表格模式的choice。

在list页面显示列表:
class QuestionAdmin(admin.ModelAdmin):
# …
list_display = (‘question_text’, ‘pub_date’, ‘was_published_recently’)

添加筛选、搜索按钮:
class QuestionAdmin(admin.ModelAdmin):
# …
list_filter = [‘pub_date’]
search_fields = [‘question_text’]

所有功能实现如下:
from django.contrib import admin
from .models import Choice, Question
class ChoiceInline(admin.TabularInline):
model = Choice
extra = 3
@admin.register(Question) #装饰器用法,取代了admin.site.register(Question, QuestionAdmin)
class QuestionAdmin(admin.ModelAdmin):
list_display = (‘question_text’, ‘pub_date’, ‘was_published_recently’)
fieldsets = [
(None, {‘fields’: [‘question_text’]}),
(‘Date information’, {‘fields’: [‘pub_date’], ‘classes’: [‘collapse’]}),
]
inlines = [ChoiceInline]
list_filter = [‘pub_date’]
search_fields = [‘question_text’]

【信号】
pre_save()

django.db.models.signals.pre_save

在model执行save方法前被调用

5个参数:

pre_save(sender,instance,raw,using,update_fields)

sender:model类

instance:保存的实例

raw:一个Boolean类型,如果model被全部保存则为True

using:使用的数据库别名

update_fields:传递的待更新的字段集合,如果没有传递,则为None
post_save()

djang.db.models.post_save

在model执行完save方法后被调用

6个参数

post_save(sender,instance,created,raw,using,update_fields)

sender:model class

instance:被保存的model实例

created:Boolean值,如果创建了一个新的记录则为True

raw:Boolean值,如果model被全部保存则为True

using:使用的数据库别名

update_fields:传递的待更新的字段集合,如果没有传递,则为Non
pre_delete()

django.db.models.signals.pre_delete

在执行model的delete()或者queryset的delete()方法前调用

pre_delete(sender,instance,using)

sender:model class

instance:被删除的实例

using:使用的数据库别名
post_delete()

django.db.models.signals.post_delete

在执行model的delete()或者queryset的delete()方法后调用

post_delete(sender, instance,using)

sender:model class

instance:被删除的实例,注意:此时,该实例已经被删除了,数据库中不再有这条记录,所以在使用这个实例的时候要格外注意

using:被使用的数据库别名

m2m_changed()

django.db.models.signals.m2m_changed

当一个model的ManyToManyField发生改变的时候被发送,严格的说,这并不是一个模型信号,因为它是被ManyToManyField发送的,但是因为它也实现了pre_save/post_save和pre_delete/post_delete,所以也在model signals中包含了。

参数:

sender:描述ManyToManyField的中间模型类,这个中间模型类会在一个many-to-many字段被定义时自动被创建。我们可以通过使用many-to-many字段的through属性来访问它

instance:被更新的多对多关系的实例。它可以是上面的sender,也可以是ManyToManyField的关系类。

action:指明作用于关系更新类型的字符串,它可以是以下几种情况:

“pre_add”/“post_add”:在向关系发送一个或多个对象前 / 后发送

“pre_remove/post_remove”:从关系中删除一个或多个对象前 / 后发送

“pre_clear/post_clear”:在关系解除之前 / 之后发送

reverse:正在修改的是正向关系或者反向关系,正向False,反向为True

model:被添加、删除或清除的对象的类

pk_set:对于add/remove等,pk_set是一个从关系中添加或删除的对象的主键 的集合, 对于clear,pk_set为None

https://www.cnblogs.com/fiona-zhong/p/9983996.html

【Xadmin学习】

以下设置都在app文件夹下的adminx.py中:
class Custommodel:
#具体设置
pass
xadmin.site.register(Custom,Custommodel)

list_display 设置显示列表,可以直接写字段名或者自定义函数
search_fields 指定哪些字段信息可以被搜索
list_filter 添加哪些字段需要进行过滤显示(添加过滤器)
ordering 显示的列表以什么进行排序 ,加‘-’表示降序
fields 不包含在内的字段不能编辑
filter_horizontal 从‘多选框’的形式改变为‘过滤器’的方式,水平排列过滤器,必须是一个 ManyToManyField类型,且不能用于 ForeignKey字段,默认地,管理工具使用下拉框 来展现外键 字段
list_editable 列表显示的时候,指定的字段可以直接页面一键编辑
readonly_fields 指定一些字段为只读,不可修改
exclude 在编辑和查看列表时指定不显示的字段
show_detail_fields 在指定的字段后添加一个显示数据详情的一个按钮
relfield_style = ‘fk-ajax’
外键变成搜索框,搜索字段search_fields里需要设置外键相关能搜索的字段
设置布局:
form_layout = (

Fieldset(u’’,

Row(‘cat’, ‘tag’),

Row(‘title’, ‘status’)

Row('display_order),

css_class = ‘unsort no_title’

),

)

设置全局样式
class BaseSetting(object):
enable_themes=True
use_bootswatch=True
xadmin.site.register(views.BaseAdminView,BaseSetting)

设置全局显示:
class GlobalSetting(object):
#页头
site_title = ‘首辰CRM客户关系管理系统’
#页脚
site_footer = ‘首辰CRM’
# menu_style = ‘accordion’
xadmin.site.register(views.CommAdminView, GlobalSetting)

自定义函数显示:
def contract_count(self):
return self.contract_set.all().count()
contract_count.short_description=“合同数量”

自定义HTML:
def go_to(self):
from django.utils.safestring import mark_safe
return mark_safe("<a href=‘contract/contract/?_rel_custom__id__exact=1’>跳转</a>")
go_to.short_description = “跳转”

重写增加删除函数:
def save_models(self): # 保存时生成汇总信息
obj = self.new_obj
obj.save()
def delete_models(self,obj_qset):
obj_qset.delete()
for obj in obj_qset.all():
def has_delete_permission(self):
return False
重写显示:
def get_model_form(self, **kwargs):
if self.org_obj is None:
self.fields = [‘username’, ‘mobile’, ‘is_staff’]

    return super().get_model_form(**kwargs)

inline显示:
class ReceiptInline(object):
model = Receipt
# style=‘tab’
extra = 0
class Contractmodel:
inlines = [ReceiptInline]

自定义布局:
form_layout = (

    Fieldset((u'通用'),
             'custom', "num",'name','singman', 'signdate', 'year',
             'total_price', 'contract_type','contract_contact',
             'update_by','comment',
             Row('createtime','updatetime'),
    css_class = 'unsort'),
    Fieldset('收款单自动汇总',
            Row( 'Accepted', 'receivable'),
             'isfinish',
             css_class='unsort',
    ),)

注册模型但隐藏菜单:
hidden_menu = True

media添加js、css:
def get_media(self):
media= super().get_media()
media+=self.vendor(‘selectize.css’,‘selectize.bootstrap3.css’)
return media
vendor中的tag分两种:
1、vendor文件夹下有的,使用xadmin/vendors.py 中的字典记录:
如: “select.css” "timepicker.css"等
2、xadmin开头,直接使用文件名即可,前提一定要放在xadmin/css 或者js文件夹中
from django.templatetags.static import static
使用这个函数可以像在模版中一样得到路径

【插件概述】
1、通过运行plugins文件夹下_init_.py文件中的plugins列表,执行(import_module)同名的pulugins py文件。
2、文件中的site.register_plugin命令,将具体的插件注册到ListAdminView等内置view下。
3、url访问具体的页面时,一方面通过admin_site.get_view_class方法将自定义的option_calss和类似ListAdminView的view_class组合起来,得到共同齐全的admin_class;另一方面,将register_plugin中注册到view_class上的plugins和option_class组合起来,同名属性、方法全部以option_class为准,得到最终能够使用的plugin_class.
4、admin_class初始化实例的过程中,通过BaseAdminView的init(self, request, *args, **kwargs)方法,执行self.base_plugins = [p(self) for p in getattr(self,“plugin_classes”, [])],将plugin_class.全部实例化,注意,实例化过程中传递了本身,所以任何plugin可以调用option_class自己的同名属性和admin_class全部的方法、属性。
5、init的self.init_plugin(*args, **kwargs)
self.init_request(*args, **kwargs),将request、user注入p实例,result = p.init_request(*args, **kwargs)又通过这个方法确定是否加入到self.plugins中。
7、所有@filter_hook装饰器修饰的方法执行时,会调用插件中的同名方法,按照插件方法的 priority 属性排序,顺序执行插件方法。
8、如果插件方法的第一个参数为 __ , 则 AdminView 方法将作为第一个参数传入, 注意, 这时还未执行该方法, 在插件中可以通过 __() 执行, 这样就可以实现插件在 AdminView 方法执行前实现一些自己的逻辑。
9、如果插件方法的第一个参数不为 __ , 则执行 AdminView 方法, 将结果作为第一个参数传入;通过这种方法,例如get_media(self,media),可以在原有插件链上增加新的效果。

【action概述】
action分为显示和执行两个逻辑:
显示:
0、action只会在列表页显示,点击list页面,基于list的admin_class被asview启动。
1、插件plugin执行的过程中,第一个就是ActionPlugin,其init_request方法返回的是bool(self.actions),如果没有任何一个actions,不会显示action的html。
2、actions由get_actions方法给出,除了默认的DeleteSelectedAction,它还会寻找self.admin_view.class.mro(),也即当前admin_view中的所有名为actions的属性,然后通过get_action方法得到action的各类显示属性。
3、action的所有属性基本为obj(包括class和funcion)、name、desription、icon,其中class类的aciton还有model_perm属性,包括view add change delete等4种权限,如果没有相应权限,会返回NOne,最终被get_actions fiter出去。(自定义修改源码后,无论是class和function,都具有model_perm属性,并且可以为函数,默认传递一个(list_class)进去,参数即为当前的list_view)。
4、通过get_list_display、get_context等方法,显示出勾选框和操作、提示按钮。
执行:
1、页面勾选了勾选框,点击某一个action按钮后,页面通过 . d o a c t i o n ( ′ d e l e t e s e l e c t e d ′ ) ; 的 j s 函 数 运 行 , .do_action('delete_selected');的js函数运行, .doaction(deleteselected);js(’#action’).val(name);
$(’#changelist-form’).submit();显而易见,它同时将名为action的单一post参数和名为_selected_action(action注册的ACTION_CHECKBOX_NAME常量)数组传递给了list页面。
2、actionpulugin插件通过post_response方法篡改正常返回的结果,进入该函数后,它通过self.actions[action]得到插件的具体信息,通过request.POST.getlist(ACTION_CHECKBOX_NAME)得到selected的id数组,通过admin_view.list_queryset.filter(pk__in=selected)得到qs。
3、调用response_action->get_model_view(ac, self.admin_view.model)方法,将刚才得到的action class初始化,然后调如果action是class,调用do_action(queryset),如果是function,调用ac(self.admin_view, self.request, queryset),将得到的结果返回给浏览器,注意,这里如果返回的结果不是HttpResponse类,将默认返回HttpResponseRedirect(request.get_full_path())

两类action使用示例:
1、类
from xadmin.plugins.actions import BaseActionView
class
MyAction(BaseActionView):

action_name = u’accept’

model_perm = ‘approve’

description = ‘同意’

def do_action(self, queryset):

for obj in queryset:
        
  ##你的操作
        
  print(obj)

return HttpResponse(‘您已同意’)

class TaskApproveSettings(ViewOnlyMixin):

actions = [MyAction,]

list_display = [‘name’,‘targets’,‘services’,‘create_by’]

xadmin.site.register(TaskApprove,TaskApproveSettings)

注意,BaseActionView是继承于ModelAdminView的,get_model_view(ac, self.admin_view.model)初始化时,也将request传入,所以它实实在在是一个ModelAdminView的实例,拥有包括request、user、app_label等所有属性和方法,同时它本身还额外拥有list_view、admin_site两个成员,在do_action方法中,还可以调用传入的queryset,该qs就是list页面选择的所有行数据。

2、函数
def update_data_src(modeladmin, request, queryset):
print(modeladmin,request,queryset)
modeladmin.message_user(“取消了任何操作!”)
return
def permission(admin_view):
return admin_view.get_model_perms()[‘change’]
update_data_src.short_description = “批量暂停进度表”
update_data_src.model_perm=permission
update_data_src.icon=“fa fa-pause”

########optionclass 略
actions = [update_data_src]
可以看到,函数只能拥有modeladmin, request, queryset三个可处理的参数。同时它的显示信息需要额外给函数变量进行注入。

【流程概述】
list页面流程:
init_request过程中调用:
self.list_display=self.get_list_display()
self.list_display_links=self.get_list_display_links()
可以重写两个方法,也可以在其他地方对变量进行修改,只要在results之前,都不影响其加载成表格。
get流程:
1、response = self.get_result_list()->make_result_list():
self.list_queryset = self.get_list_queryset()-》queryset = self.queryset()
也就是说,前期qs的处理,实际上是get_result_list()函数独立完成的。
在这个函数中设置self.result_list属性为分页后的object_list,需要注意的是,如果进行了返回,因为这一句存在 if response:return response,会直接返回页面,插件可以获取此方法,重写后得到json数据。
2、context = self.get_context():
‘result_headers’: self.result_headers(),
‘results’: self.results() 调用result->result_row->result_item获取列格式数据
self.get_response(context, *args, **kwargs)
该方法在list中是一个空方法,但是可以给插件捕获使用,在model_list.html之前返回自定义的内容。
post流程:
return post_result_list() or …
1、post_result_list()-》self.make_result_list()
流程和get一致了,也可以捕获之前返回非row格式内容。
2、self.post_response(*args, **kwargs)
也是一个空函数,供给插件捕获使用。
3、or self.get(request, *args, **kwargs) 最后前面没有设置,返回get 的list界面。
tips:插件捕获时,可以调用baseadminobject的render_response(self, content, response_type=‘json’)方法返回json格式数据。或者调用template_response(self, template, context)方法返回render数据。
result结构:
results() 返回一个results列表,列表元素为ResultRow()对象。
ResultRow() 实际上就是一个字典,主要有is_display_first、object两个键和.cells一个属性。object就是obj对象,cells是result_item对象实例。
result_item:
text:显示内容 wraps:class的list row:所属的哪一个ResultRow对象 field_name:当前字段名
field:当前字段,如果不是函数字段为NONE attr:非字段时的函数对象 value:字段或函数的值

编辑、添加页面流程:
add-get
instance_forms()->
form_obj = self.model_form(**self.get_form_datas())
重点在 get_form_datas()
get时返回{‘initial’: initial},将get参数中的初始化参数构成初始化表单
post返回{‘data’: self.request.POST, ‘files’: self.request.FILES} 将post的数据构成陈品表单
->self.setup_forms()
form_obj.helper = helper
->get_response()-> self.get_context()调用父类modelformview的同名方法,将form存入,然后渲染

add-post
instance_forms()-》self.setup_forms()得到form_obj
valid_forms()-》save_forms()-》save_models()-》save_related()
post_response() 根据post参数返回不同页面

update-get
流程和add一样
get_form_datas()-}{‘instance’: self.org_obj}直接将obj对象转化为form
org_obj对象初始化 init_request时已经构造

update-post
{‘data’: self.request.POST, ‘files’: self.request.FILES}
{‘instance’: self.org_obj}
以instance为基准,将data修改后的内容补充,可以通过has_changedata 和changedata查看字段有无修改。
其他一致

【layout概述】
crispy基础组件:
Div 将内容环绕div标签,可以配置div的class和属性:
参数: css_class cls的字符串
css_id attr
Field 表单组件用Field(“x”)表示一个字段。有css_class wrapper_class属性,kwargs的其余字段将修饰在表单逐渐上,作为属性显示。
可以修饰多个字段Field(“x”,“y”),但都是分开渲染的,不同的是
kwargs中的设置会设置到每一个表单上

Fieldset bs的panel
参数: legend就是title description:跟随在title后面的小字描述
css_class panel最外层div的class
css_id panel最外层div的attr,如style等
fields content中的具体内容
Row form-inline form-group 的div,除了
fields没有其他参数,div的参数照样继承
会自动将*fields按数量进行12列平分,添加上col-sm-%d的标签

Col column’, 'form-column formColumn id col col-sm-%d % span
一个分列的div标签,其中id是自定义的css_class(字符串),span是col的列数,默认为6
参数中如果有horizontal,将装饰form-horizontal水平表单
Main column form-column main col col-sm-9 form-horizontal
Side 必须和Main搭配column form-column sidebar col col-sm-3

Container form-container row clearfix

InputGroup 原来的渲染是form-group(labelcontrol-label inputform-control),使用这个组键可以在input上wrap一个controls->inputgroup,将input和自己增加的input修饰成一个输入框组。
例子: InputGroup(“type”,’@@’,“woaini”)
"@@"表示原有字段,后面是自己添加的文字或组键,将生成<span class=“input-group-addon”>woaini</span>
PrependedText PrependedText(field, text, **kwargs)等于InputGroup(field, text, ‘@@’, **kwargs)
AppendedText (field, text, **kwargs)等于InputGroup(field, ‘@@’,text, **kwargs)
PrependedAppendedText field, prepended_text=None, appended_text=None, *args, **kwargs 不再赘述

list_display=[] #要显示的字段
search_fields=[] #搜索的字段
list_filter = [] #过滤器
list_display_links`
show_detail_fields i和链接字段
date_hierarchy =[‘publication_date’] #添加过滤(这里是过滤日期)
ordering = [’-publication_date’,] #排序(这里以日期排序,加‘-’表示降序)
filter_horizontal = (‘authors’,) #filter_horizontal 从‘多选框’的形式改变为‘过滤器’的方式,水平排列过滤器,必须是一个 ManyToManyField类型,且不能用于 ForeignKey字段,默认地,管理工具使用下拉框 来展现外键 字段
filter_vertical = [‘authors’,]#同上filter_horizontal,垂直排列过滤器
raw_id_fields = [‘publisher’,] #将ForeignKey字段从‘下拉框’改变为‘文本框’显示
list_editable = [‘csdevice’] #在列表页可直接编辑的字段
model_icon = ‘fa fa-user-secret’ #图标样式
style_fields = {‘csdevice’: ‘m2m_transfer’,‘csservice’: ‘ueditor’,} #字段显示样式
refresh_times = [10, 60] #自动刷新时间
show_detail_fields=[‘ttdsn’] #在指定的字段后添加一个显示数据详情的一个按钮
relfield_style = ‘fk-ajax’ #涉及到外键下拉的时候使用ajax搜索的方式而不是全部列出的方式,比如在分类下拉很多的情况下,这个功能就很好用
free_query_filter=[‘字段1’,‘字段2’,…]#默认为 True , 指定是否可以自由搜索. 如果开启自由搜索, 用户可以通过 url 参数来进行特定的搜索
exclude=[‘字段1’,‘字段2’,…]#隐藏字段
aggregate_fields = {“expire”: “max”}# 列聚合,在list表格下面会增加一行统计的数据,可用的值:“count”,“min”,“max”,“avg”, “sum”

添加数据时候,一步一步提供数据,分块显示

wizard_form_list = [
(“基础信息”, (“name”, “contact”, “telphone”, “address”)),
(“其它信息”, (“customer_id”, “expire”, “description”)),
]
grid_layouts = (“table”, “thumbnails”) #列表的布局方式,是以表格一行一条的方式还是类似于缩略图的方式展示的
list_per_page = 50 # 每页显示数据的条数
list_max_show_all = 200 #每页最大显示数据的条数

django时间显示:
更改settings.py中的I10N为False, 取消本地化. 然后在settings.py中更改DATETIME_FORMAT.
直接更改Python目录/site-packages/django/conf/locale/zh_Hans/formats.py文件中的DATETIME_FORMAT为Y年n月j日 H:i:s.

【vendor和media】
vendor(name,name)方法中,name有两类写法:
1、startswith(‘xadmin’),形如xadmin.utill.getdata.js
会自动转化为"xadmin/js/xadmin.utill.getdata.js,所以需要把文件放在static/xadmin/js文件夹下
2、不以xadmin开头,以xadmin/vendors.py文件中定义的vendors(dict)开头,形如:
jquery-ui-sortable.js font-awesome.css
实际上执行了vendors[“jquery-ui-sortable”][“js”][“mode”],取到最终文件名,mode根据debug自动判断,取min文件还是非min文件。

【【部署笔记】】:
Debug = False
Allow_host=[’*’]
STATIC_ROOT = os.path.join(BASE_DIR, “/var/www”)
python manage.py collectstatic

<uwsgi>
<socket>:8000</socket>
<chdir>/home/user/project/hello</chdir>
<module>hello.wsgi</module>
<processes>4</processes> <!-- 进程数 -->
<daemonize>uwsgi.log</daemonize>
</uwsgi>

根目录下新建xml文件:
<uwsgi>
<socket>:8000</socket>
<chdir>/www/wwwroot/sccrm/</chdir>
<module>scwork.wsgi</module>
<processes>4</processes> <!-- 进程数 -->
<daemonize>uwsgi.log</daemonize>
<vacuum>true</vacuum>
<pidfile>uwsgi.pid</pidfile>
<py-autoreload >1</py-autoreload >
<disable-logging>true</disable-logging>
</uwsgi>

nginx配置文件:
server
{
listen 88;
server_name sccrm.com 152.136.146.28;
index index.php index.html index.htm default.php default.htm default.html;
root /www/wwwroot/sccrm;
access_log /www/wwwlogs/sccrm.com.log;
error_log /www/wwwlogs/sccrm.com.error.log;
client_max_body_size 75M;

  location / { 
         include uwsgi_params;
         uwsgi_pass 127.0.0.1:8000;
         uwsgi_read_timeout 2;
   }   
   location /static {
        expires 30d;
        autoindex on; 
        alias //www/wwwroot/sccrm/extra_apps/xadmin/static/;
     }

}

killall -9 uwsgi
uwsgi -x socket.xml
service nginx reload

uwsgi --reload uwsgi.pid
#其中uwsgi.pid是我的uwsgi自动生成的文件,即进程的pid文件。

uwsgi --ini uwsgi.ini

重启:

uwsgi --reload uwsgi.pid

停止:

uwsgi --stop uwsgi.pid

【快捷tips】
from django.template import loader
loader.render_to_string 可以把模版渲染成字符串返回

from django.views.decorators.csrf import csrf_exempt
@csrf_exempt 视图函数不需要再验证csrf

【import_export】

+++ settings.py +++
INSTALLED_APPS = (

‘import_export’,
)

+++ model.py +++
from django.db import models

class Foo(models.Model):
name = models.CharField(max_length=64)
description = models.TextField()

+++ adminx.py +++
import xadmin
from import_export import resources
from .models import Foo

class FooResource(resources.ModelResource):

class Meta:
    model = Foo
    # fields = ('name', 'description',)
    # exclude = ()

@xadmin.sites.register(Foo)
class FooAdmin(object):
import_export_args = {‘import_resource_class’: FooResource, ‘export_resource_class’: FooResource}

class Prg_base_resource(resources.ModelResource):
contract = Field(column_name=“合同编号”, attribute=“contract”, widget=ForeignKeyWidget(Contract, ‘name’))

def __init__(self):
    super(Prg_base_resource, self).__init__()
    # 获取所以字段的verbose_name并存放在字典
    field_list = Prgsheet._meta.fields
    self.vname_dict = {}
    for i in field_list:
        self.vname_dict[i.name] = i.verbose_name

def get_export_fields(self):
    fields = self.get_fields()
    for field in fields:
        field_name = self.get_field_name(field)
        # 如果我们设置过verbose_name,则将column_name替换为verbose_name。否则维持原有的字段名
        if field_name in self.vname_dict.keys():
            field.column_name = self.vname_dict[field_name]
    return fields

model = Prgsheet
skip_unchanged = True
report_skipped = False
import_id_fields = (‘contract’,)
export_order =fields

【User】
contrib.auth authenticate,login
这两个方法,一个验证,一个登陆,将user对象注入到request之中。
user对象有一个is_authenticate,可以返回user是否经过验证。
验证密码:user.check_password(psd)

【Xadmin源码】
在任意的app下的apps.py中的Config类下自定义ready()方法,并且调用autodiscover_modules。
django.utils.module_loading.autodiscover_modules
该方法可以自动扫描各APP下每一个文件,如:
def ready(self):
utodiscover_modules(‘xadmin’)

【notifications】
from notifications.signals import notify
notify.send(actor, recipient, verb, action_object, target, level, description, public, timestamp, **kwargs)
actor:必须,任意类型,类似于instance,表示发出信号的对象,可以使用.sent()方法查询该对象发出的所有信息。
recipient:必须,组或用户或用户的queryset类型,接收人。
verb:必须:该消息的文字介绍。
以下为非必须参数:
action_object: 执行通知的对象
target:链接到动作的对象
description: 字符串,详细内容。
以下为有默认值的参数:
level: One of Notification.LEVELS (‘success’, ‘info’, ‘warning’, ‘error’) (default=info)
public: An boolean (default=True). (Optional)
timestamp: An tzinfo (default=timezone.now()). (Optional)
ex:
杜赛 (actor) 在 Django搭建个人博客 (target) 中对 你 (recipient) 发表了 (verb) 评论 (action_object)
方法:
qs.sent()
qs.unread()
qs.read()
qs.mark_all_as_read() | qs.mark_all_as_read(recipient)
qs.mark_all_as_unread() | qs.mark_all_as_unread(recipient)
qs.deleted()
Return all notifications that have deleted=True, filtering the current queryset. Must be used with SOFT_DELETE=True.
qs.active()
Return all notifications that have deleted=False, filtering the current queryset. Must be used with DELETE=True.
qs.mark_all_as_deleted() | qs.mark_all_as_deleted(recipient)
qs.mark_all_as_active() | qs.mark_all_as_active(recipient)
ex:
Notification.objects.unread()
user = User.objects.get(pk=pk)
user.notifications.unread()

模版方法:
{% load notifications_tags %}
{% notifications_unread %}
{% notifications_unread as unread_count %}

{% if unread_count %}
You have <strong>{{ unread_count }}</strong> unread notifications.
{% endif %}

API:
api/unread_count/:{“unread_count”:1}
api/unread_list/:
{
“unread_count”:1,
“unread_list”:[–list of json representations of notifications–]
}

【tips】
【django2.2bug】
AttributeError: ‘str’ object has no attribute ‘decode’
django\db\backends\mysql\base.py文件:
if version < (1, 3, 3):
raise ImproperlyConfigured(“mysqlclient 1.3.3 or newer is required; you have %s” % Database.version)注释掉

db/backends/mysql/operations.py", line 146, in last_executed_query
if query is not None:
query = query.decode(errors=‘replace’)修改decode为encode 或者注视掉这两行,建议2

【django后台日期格式】
django/conf/locale/zh_Hans/formats.py文件中的DATETIME_FORMAT为Y年n月j日 H:i:s

【crispy.同步更新attr】
crispy_forms/templatetags/crispy_forms_field.py
113:增加widget.attrs.update(field.field.widget.attrs)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的Django学习教程: 1. 首先,确保你已经安装了Python和Django。你可以在官方网站上下载并安装Django:https://www.djangoproject.com/download/ 2. 创建一个新的Django项目。在命令行中,进入你想要创建项目的目录,并运行以下命令: ```shell django-admin startproject myproject ``` 这将创建一个名为myproject的新项目。 3. 进入项目目录: ```shell cd myproject ``` 4. 创建一个新的应用程序。在命令行中运行以下命令: ```shell python manage.py startapp myapp ``` 这将创建一个名为myapp的新应用程序。 5. 在项目的settings.py文件中,将新创建的应用程序添加到INSTALLED_APPS列表中: ```python INSTALLED_APPS = [ ... 'myapp', ] ``` 6. 在应用程序的目录中,创建一个名为urls.py的文件,并添加以下内容: ```python from django.urls import path from . import views urlpatterns = [ path('', views.index, name='index'), ] ``` 这将创建一个名为index的视图函数,并将其与根URL关联。 7. 在应用程序的目录中,创建一个名为views.py的文件,并添加以下内容: ```python from django.http import HttpResponse def index(request): return HttpResponse("Hello, Django!") ``` 这将定义一个名为index的视图函数,它将返回一个简单的“Hello, Django!”消息。 8. 在项目的urls.py文件中,将应用程序的URL包含在urlpatterns中: ```python from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('', include('myapp.urls')), ] ``` 这将将应用程序的URL与根URL关联起来。 9. 运行开发服务器。在命令行中运行以下命令: ```shell python manage.py runserver ``` 这将启动Django开发服务器,并在本地主机上的默认端口上运行。 10. 在浏览器中访问http://localhost:8000/,你应该能够看到“Hello, Django!”消息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值