python Web项目布局分析
元旦本来在家躺着摆烂的,突然有人喊我帮他做个django的期末作业,还说要v我50(什
我看起来像是那种会向好朋友伸手要钱的人吗????
只是觉得以后要自己也要做其他项目,之前也只接触过一丢丢flask(半懂不懂的
所以我就答应了下来(是为了提升自己(反复强调
但是他的deadline就在第二天中午,最后还是没肝完(残念
但是他把他从小红书上买来的代码发我了(
所以这篇博客就是来看看别人写的代码和自己写的的代码的区别在哪,浅浅总结一下。
简单项目实践(Django)
要求
自己写的
项目的布局如下:
1 | index |
这里项目的名称为index
。有两个app
分别为exam
和user
。templates
里放的是网页等静态文件。
路由和views是这样设置的:
index
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20# 路由
path('admin/', admin.site.urls),
path('', views.login),
path('login/', views.login, name='login'),
path('home/', views.home, name='home'),
path('register/', views.register, name='register'),
path('user/', include('user.urls'))
# views
def index(request):
return HttpResponse('testindex')
def login(request):
return render(request, 'login.html')
def home(request):
return HttpResponse('testHome')
def register(request):
return render(request, 'register.html')user
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30# 路由
path('initdb/', views.InitDB),
path('login/', views.login)
# views
def InitDB(request):
test1 = Teacher(name='teacher1', classID='1', passwd='123')
test1.save()
test2 = Student(name='student2', classID='2', passwd='123')
test2.save()
print('数据库初始化成功')
return redirect('login')
def login(request):
if request.POST:
username = request.POST.get('username')
# 学生登录
try:
s = Student.objects.filter(name=username)[0]
if s.passwd == request.POST.get('password'):
return HttpResponse('登录成功')
except:
pass
try:
t = Teacher.objects.filter(name=username)[0]
if t.passwd == request.POST.get('password'):
return HttpResponse('登录成功')
except:
pass
return HttpResponse('登录失败')exam
还没写完(
这里是这样想的,index
用作加载各个页面,然后app看作程序用的接口,然后每个app管理不同类型的数据,如:
- user就是用户验证,操作用户数据库。
- exam就是管理题目,有对题目数据库操作的接口。
然后在各个app内定义各自的models,如:
user_models.py
1
2
3
4
5
6
7
8
9
10
11
12
13class Teacher(models.Model):
# id = models.CharField(max_length=32, primary_key=True)
name = models.CharField(max_length=32)
classID = models.CharField(max_length=32)
passwd = models.CharField(max_length=32)
role = models.IntegerField(default=0)
class Student(models.Model):
# id = models.CharField(max_length=32, primary_key=True)
name = models.CharField(max_length=32)
classID = models.CharField(max_length=32)
passwd = models.CharField(max_length=32)
role = models.IntegerField(default=1)exam_models.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class Exam(models.Model):
title = models.CharField(max_length=128)
time = models.IntegerField(default=3600)
deadline = models.DateTimeField()
# 如果是选择题就正常abcd选项,多选题答案传入用逗号隔开,选择题只有ab选项,问答题只有题干
# type 0是单选,1是多选,2是判断,3是大题
class Question(models.Model):
ExamID = models.ForeignKey(Exam, on_delete=models.CASCADE)
stem = models.CharField(max_length=1000)
a = models.CharField(max_length=256)
b = models.CharField(max_length=256)
c = models.CharField(max_length=256)
d = models.CharField(max_length=256)
correct = models.CharField(max_length=32)
type = models.IntegerField(default=0)
但是这样就会出现一种问题。我有一些操作(或表)是需要同时对两个app进行操作的。
如:老师(user)布置的考试(exam)。
这时候就需要在一边同时导入两个模型,那么现在问题就来了,我为啥不把这俩模型写一起。
这就又回到了我分开写的初衷上了:为了代码清晰,且不累赘。
这就不得不说,在写这个程序前,我还废掉了两个,第一个完全就是因为不熟悉框架。第二个是因为我以页面为单位来分隔app,结果就导致了app非常多,login一个,home一个,register一个,最后看着实在糟心就干脆重新写了。
这就有一个矛盾了,如果写在一起,容易让代码变得很混乱,不够清晰。
但是分开来写,一个问题就是上面的同时要两个模型的情况会很难受,如果我现在不管这个,直接按照代码的用途写在各自的app下,还可能会出现分块太多,过于臃肿的情况。
(这个疑问困扰我好久了,现在接触到的项目真的太少了,具体项目要按照什么个规则来写还是很模糊(要是有大佬带就好了5555)
自己的代码处刑完了,现在来看看别人的吧。。。
别人写的
项目的布局如下:
1 | stuexam2 |
这里项目的名称为stuexam
。有两个app
分别为my_app
和exam
。templates
里放的是网页模板,static
里是静态文件,utils
里放的是需要的表单。
来看看他的路由和views(部分):
stuexam
1
2
3
4
5
6# 路由
path('admin/', admin.site.urls),
path('', include('my_app.urls')),
path('exam/', include('exam.urls')),
# views
无my_app
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31# 路由
path('login/', views.login, name='login'),
path('', views.login, name='login'),
path('logout/', views.logout, name='logout'),
path('register/', views.register, name='register'),
path('index/', views.index, name='index'),
# views
def login(request):
'''登录'''
if request.method == 'GET':
form = LoginForm()
return render(request ,'login.html', {'form':form})
form = LoginForm(data=request.POST)
if form.is_valid():
username = form.cleaned_data['username']
user_object = models.User.objects.filter(
**form.cleaned_data #此时form.cleaned_data没有code
).first()
if not user_object:
form.add_error('password','The username and password are incorrect') #增加一个错误提示把错误绑定在password字段
return render(request,'login.html' , {'form' : form})
request.session['info'] = {'username': user_object.username,
'id': user_object.id} # 写入服务器的session,同时随机数存入了用户的cookie
messages.success(request, 'Login successful')
return redirect('my_app:index')
return render(request,'login.html' , {'form' : form})
def index(request):
exams = Exam.objects.all()
return render(request, 'index.html', {'exams': exams})
...exam
1
2
3
4
5
6
7
8
9# 路由
path('survey/<int:examid>', views.survey, name='survey'),
# views
def survey(request,examid):
questions = Exam.objects.get(id=examid).questions.all()
context = {
'questions': questions,
}
return render(request, 'survey.html', context)
他这里的项目目录是不做具体的页面跳转的(没有views),全部都是写在app里的。
这样写会更清晰一点(学了!)。
然后其他的其实和我的想法也差不多,但他是在页面上直接实现相应的功能(我是当作api来用),用get和post来区分页面的不同功能。
感觉各有好处吧,如果像他这样做的话要前后端分离应该就会比较麻烦(不过小项目的话有前后端分离的必要吗)。
其他不同的原因主要就是对这个模板的熟练程度的不同了。
github热门项目分析(Flask)
gpt4free
1 | g4f |
主要的代码在server文件夹中,这个项目是单页面的,按上面类似Django来看的话,这主要分为了两个模块(app)。
首先他在app.py
里创建flask app。(相当于上面的project)
1 | app = Flask(__name__, template_folder='./../client/html') |
然后建了两个模块(相当于上面的app),backend.py
,website.py
。下面是它们的路由:
website.py(主要用作页面跳转)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18self.routes = {
'/': {
'function': lambda: redirect('/chat'),
'methods': ['GET', 'POST']
},
'/chat/': {
'function': self._index,
'methods': ['GET', 'POST']
},
'/chat/<conversation_id>': {
'function': self._chat,
'methods': ['GET', 'POST']
},
'/assets/<folder>/<file>': {
'function': self._assets,
'methods': ['GET', 'POST']
}
}backend.py
(主要用作后端处理)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26self.routes = {
'/backend-api/v2/models': {
'function': self.models,
'methods' : ['GET']
},
'/backend-api/v2/providers': {
'function': self.providers,
'methods' : ['GET']
},
'/backend-api/v2/version': {
'function': self.version,
'methods' : ['GET']
},
'/backend-api/v2/conversation': {
'function': self._conversation,
'methods': ['POST']
},
'/backend-api/v2/gen.set.summarize:title': {
'function': self._gen_title,
'methods': ['POST']
},
'/backend-api/v2/error': {
'function': self.error,
'methods': ['POST']
}
}
这里是把它们都写在了一个类中,分别为Website类和Backend_Api类,然后在类中定义了各自的方法。
然后由__init__.py
将两个类导入到app中。
感觉这种写法确实好啊,就是这个只是单页面的,多页面还是不大清楚要咋写,还有这种写法还是要多练55555。
CTFd
这个文件树有点大我就放部分吧。
1 | CTFd |
感觉这个就属于那种有点乱的范畴了。。。已经不大清楚哪是哪了。。
但是大致上可以看出它也是按照页面来分的,分别写在了views.py
,users.py
,teams.py
…文件里,分别处理各自页面下的跳转。然后也是在路由下面直接写好了程序的功能。
然后像一些小的,不需要没有其他功能的页面,它就直接放在了views.py
里,如robots.txt等。
跟g4f不同,它这里用的是flask里的蓝图来做的(个人还是更喜欢g4f的那种办法,但是感觉蓝图的要求稍微比g4f的简单点)。
总结
项目布局可以以页面为单位来分,每个功能较多的页面可以单独建一个文件来写,小的页面则可以直接写在一起。还有一些后端实现也可以单独放一个文件,与页面的划分类似。
总结之总结:菜就多练,蚁钳是蚁钳,蟹仔是蟹仔。不练就一直是菜。