├── .gitignore
├── README.md
├── accounts
├── __init__.py
├── admin.py
├── api
│ ├── __init__.py
│ ├── serializers.py
│ ├── tests.py
│ └── views.py
├── apps.py
├── migrations
│ └── __init__.py
├── models.py
├── tests.py
└── views.py
├── doc
├── PushModel.jpg
├── demo-1.jpg
└── demo-2.jpg
├── manage.py
├── requirements.txt
└── twitter
├── __init__.py
├── asgi.py
├── settings.py
├── urls.py
└── wsgi.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.box
2 | .vagrant
3 | Vagrantfile
4 |
5 | # Byte-compiled / optimized / DLL files
6 | __pycache__/
7 | *.py[cod]
8 | *$py.class
9 |
10 | # C extensions
11 | *.so
12 |
13 | # Distribution / packaging
14 | .Python
15 | build/
16 | develop-eggs/
17 | dist/
18 | downloads/
19 | eggs/
20 | .eggs/
21 | lib/
22 | lib64/
23 | parts/
24 | sdist/
25 | var/
26 | wheels/
27 | pip-wheel-metadata/
28 | share/python-wheels/
29 | *.egg-info/
30 | .installed.cfg
31 | *.egg
32 | MANIFEST
33 |
34 | # PyInstaller
35 | # Usually these files are written by a python script from a template
36 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
37 | *.manifest
38 | *.spec
39 |
40 | # Installer logs
41 | pip-log.txt
42 | pip-delete-this-directory.txt
43 |
44 | # Unit test / coverage reports
45 | htmlcov/
46 | .tox/
47 | .nox/
48 | .coverage
49 | .coverage.*
50 | .cache
51 | nosetests.xml
52 | coverage.xml
53 | *.cover
54 | *.py,cover
55 | .hypothesis/
56 | .pytest_cache/
57 |
58 | # Translations
59 | *.mo
60 | *.pot
61 |
62 | # Django stuff:
63 | *.log
64 | local_settings.py
65 | db.sqlite3
66 | db.sqlite3-journal
67 |
68 | # Flask stuff:
69 | instance/
70 | .webassets-cache
71 |
72 | # Scrapy stuff:
73 | .scrapy
74 |
75 | # Sphinx documentation
76 | docs/_build/
77 |
78 | # PyBuilder
79 | target/
80 |
81 | # Jupyter Notebook
82 | .ipynb_checkpoints
83 |
84 | # IPython
85 | profile_default/
86 | ipython_config.py
87 |
88 | # pyenv
89 | .python-version
90 |
91 | # pipenv
92 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
93 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
94 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
95 | # install all needed dependencies.
96 | #Pipfile.lock
97 |
98 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
99 | __pypackages__/
100 |
101 | # Celery stuff
102 | celerybeat-schedule
103 | celerybeat.pid
104 |
105 | # SageMath parsed files
106 | *.sage.py
107 |
108 | # Environments
109 | .env
110 | .venv
111 | env/
112 | venv/
113 | ENV/
114 | env.bak/
115 | venv.bak/
116 |
117 | # Spyder project settings
118 | .spyderproject
119 | .spyproject
120 |
121 | # Rope project settings
122 | .ropeproject
123 |
124 | # mkdocs documentation
125 | /site
126 |
127 | # mypy
128 | .mypy_cache/
129 | .dmypy.json
130 | dmypy.json
131 |
132 | # Pyre type checker
133 | .pyre/
134 | /.pycharm_helpers/
135 | .idea
136 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Twitter 后端系统 - Python 项目实战
2 |
3 | 
4 |
5 | |:books: |:bulb: |:house: |:rocket: |:checkered_flag:|:email:|
6 | | :--------: | :---------: | :------: | :------: | :------: | :------: |
7 | | [介绍](#介绍) | [技术栈](#技术栈) | [信息流系统](#信息流系统) | [演示](#演示) | [后续内容](#后续内容) | [联系作者](#联系作者) |
8 |
9 | ## 介绍
10 | **Twitter 后端系统**:从零设计一个高并发的信息流系统,完整搭建对标P8(L5)水准的项目,可用于毕设、简历等
11 |
12 | **涉及到的重点知识包括:**
13 | - Pull & Push 模型分析对比
14 | - 高性能系统必备消息队列 Message Queue 应用场景
15 | - Denormalization 去标准化(冗余存储技术)
16 |
17 | **涉及的面试难点包括:**
18 | - 如何分别测试登录用户和未登录用户?
19 | - 如何做反向查询?
20 | - 如何设计数据库表达?
21 | - 如何让部分用户看到某个新功能,其他用户看到的就是功能?(灰度测试)
22 | - comments的API该如何设计?
23 |
24 | **建议访问课程以获取更多项目源码:**
25 | [https://www.jiuzhang.com/course/89/](https://www.jiuzhang.com/course/89/?utm_source=sc-github-zyq "https://www.jiuzhang.com/course/89/")
26 |
27 | ## 技术栈
28 | | 技术 | 官网 |
29 | |----------------------------------|--------------------------------------------------------------------------------------------|
30 | |Django |[https://www.djangoproject.com/](https://www.djangoproject.com/) |
31 | |Django REST framework |[https://www.django-rest-framework.org/](https://www.django-rest-framework.org/) |
32 | |MySQL |[https://www.mysql.com/](https://www.mysql.com/) |
33 | |HBase |[https://hbase.apache.org/](https://hbase.apache.org/) |
34 | |Redis |[https://redis.io/](https://redis.io/) |
35 | |Memcached |[http://memcached.org/](http://memcached.org/) |
36 | |RabbitMQ |[https://www.rabbitmq.com/](https://www.rabbitmq.com/) |
37 |
38 | ## 信息流系统
39 | 在设计信息流系统时,一般有两种信息流模式:`拉(pull)模式` 和 `推(push)模式`,如果去设计一个如 Tweet 的社交网络服务系统:
40 |
41 |
42 | `拉(pull)模式`:在 `拉(pull)模式`,下一个人发帖只在 Tweet 表单中产生一条新记录。
43 |
44 | `推(push)模式`:在 `推(push)模式`,下一个人发帖除了在 Tweet 表单中产生一条新纪录,还会产生 N 条 NewsFeed 记录,N 是粉丝的个数。
45 |
46 | 本项目选择使用 `推(push)模式`,流程图如下:
47 |
48 | 
49 |
50 | ## 演示
51 |
52 | 本项目为 Django 前后端分离项目,以后端 API 开发为主,界面基于 Django REST framework,相关演示图如下:
53 |
54 | 
55 |
56 |
57 |
58 | 
59 |
60 | ## 后续内容
61 |
62 | * 推文模块 Tweet
63 | * 好友关系模块 Friendship
64 | * 信息交互模块 NewsFeed
65 | * 代码结构中引入 Service 处理 views 和 models 之间的复杂逻辑
66 | * 评论模块 Comment
67 | * 用 decorators 实现丢失参数检测的,优化代码逻辑
68 | * 在 Tweet 中带上所有的评论信息
69 | * 点赞模块 Like
70 | * 设计一个通用的点赞类,可以同时记录对 Tweet 的点赞和对 Comment 的点赞
71 | * 使用 Serializer 的继承关系避免重复代码
72 | * 用 django-notification 增加评论点赞关注的消息提醒
73 | * 基于第三方开源项目自定制 API
74 | * 使用 Service 类包装复杂逻辑
75 | * 图像上传
76 | * 增加 UserProfile Model 实现头像,昵称等信息的存储
77 | * django 的 listener 机制
78 | * OneToOneField
79 | * Tweet 图像上传
80 | * 翻页 Pagination 与缓存 Cache
81 | * 实现 user timeline 和 newsfeed 的翻页 Pagination
82 | * 通过缓存优化好友关系与用户信息
83 | * 使用 Redis 缓存用户推文
84 | * 获取某个用户的 tweets
85 | * QuerySet 惰性加载
86 | * Redis RPUSH 操作
87 | * 增加 Cache 来加速 NewsFeed 的访问效率
88 | * 增加 Message Queue 来实现 fanout
89 | * Django 接入消息队列的第三方库:Celery
90 | * 增加限流器 Rate Limiter 用于限制恶意访问和减少不必要的数据库操作
91 | * 安装 Django RateLimiter 来进行访问频次限制
92 | * ......
93 |
94 | ## 联系作者
95 |
96 | 添加个人微信号发送关键词【Github】获取更多源码
97 |
98 | 
99 |
--------------------------------------------------------------------------------
/accounts/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ninechapter-algorithm/django-twitter/958a0d14e11ab4f7f54a22f75f412d2e3ffe55f8/accounts/__init__.py
--------------------------------------------------------------------------------
/accounts/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 |
--------------------------------------------------------------------------------
/accounts/api/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ninechapter-algorithm/django-twitter/958a0d14e11ab4f7f54a22f75f412d2e3ffe55f8/accounts/api/__init__.py
--------------------------------------------------------------------------------
/accounts/api/serializers.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth.models import User
2 | from rest_framework import serializers, exceptions
3 |
4 |
5 | class UserSerializer(serializers.ModelSerializer):
6 | class Meta:
7 | model = User
8 | fields = ('username', 'email')
9 |
10 |
11 | class SignupSerializer(serializers.ModelSerializer):
12 | username = serializers.CharField(max_length=20, min_length=6)
13 | password = serializers.CharField(max_length=20, min_length=6)
14 | email = serializers.EmailField()
15 |
16 | class Meta:
17 | model = User
18 | fields = ('username', 'email', 'password')
19 |
20 | def validate(self, data):
21 | # TODO 增加验证 username 是不是只由给定的字符集合构成
22 | if User.objects.filter(username=data['username'].lower()).exists():
23 | raise exceptions.ValidationError({
24 | 'message': 'This email address has been occupied.'
25 | })
26 | if User.objects.filter(email=data['email'].lower()).exists():
27 | raise exceptions.ValidationError({
28 | 'message': 'This email address has been occupied.'
29 | })
30 | return data
31 |
32 | def create(self, validated_data):
33 | username = validated_data['username'].lower()
34 | email = validated_data['email'].lower()
35 | password = validated_data['password']
36 |
37 | user = User.objects.create_user(
38 | username=username,
39 | email=email,
40 | password=password,
41 | )
42 | return user
43 |
44 |
45 | class LoginSerializer(serializers.Serializer):
46 | username = serializers.CharField()
47 | password = serializers.CharField()
48 |
--------------------------------------------------------------------------------
/accounts/api/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 | from rest_framework.test import APIClient
3 | from django.contrib.auth.models import User
4 |
5 |
6 | LOGIN_URL = '/api/accounts/login/'
7 | LOGOUT_URL = '/api/accounts/logout/'
8 | SIGNUP_URL = '/api/accounts/signup/'
9 | LOGIN_STATUS_URL = '/api/accounts/login_status/'
10 |
11 |
12 | class AccountApiTests(TestCase):
13 |
14 | def setUp(self):
15 | self.client = APIClient()
16 | self.user = self.createUser(
17 | username='admin',
18 | email='admin@jiuzhang.com',
19 | password='correct password',
20 | )
21 |
22 | def createUser(self, username, email, password):
23 | return User.objects.create_user(username, email, password)
24 |
25 | def test_login(self):
26 | response = self.client.get(LOGIN_URL, {
27 | 'username': self.user.username,
28 | 'password': 'correct password',
29 | })
30 | self.assertEqual(response.status_code, 405)
31 |
32 | response = self.client.post(LOGIN_URL, {
33 | 'username': self.user.username,
34 | 'password': 'wrong password',
35 | })
36 | self.assertEqual(response.status_code, 400)
37 |
38 | response = self.client.get(LOGIN_STATUS_URL)
39 | self.assertEqual(response.data['has_logged_in'], False)
40 |
41 | response = self.client.post(LOGIN_URL, {
42 | 'username': self.user.username,
43 | 'password': 'correct password',
44 | })
45 | self.assertEqual(response.status_code, 200)
46 | self.assertNotEqual(response.data['user'], None)
47 | self.assertEqual(response.data['user']['email'], 'admin@jiuzhang.com')
48 |
49 | response = self.client.get(LOGIN_STATUS_URL)
50 | self.assertEqual(response.data['has_logged_in'], True)
51 |
52 | def test_logout(self):
53 | self.client.post(LOGIN_URL, {
54 | 'username': self.user.username,
55 | 'password': 'correct password',
56 | })
57 | response = self.client.get(LOGIN_STATUS_URL)
58 | self.assertEqual(response.data['has_logged_in'], True)
59 |
60 | response = self.client.get(LOGOUT_URL)
61 | self.assertEqual(response.status_code, 405)
62 |
63 | response = self.client.post(LOGOUT_URL)
64 | self.assertEqual(response.status_code, 200)
65 |
66 | response = self.client.get(LOGIN_STATUS_URL)
67 | self.assertEqual(response.data['has_logged_in'], False)
68 |
69 | def test_signup(self):
70 | data = {
71 | 'username': 'someone',
72 | 'email': 'someone@jiuzhang.com',
73 | 'password': 'any password',
74 | }
75 | response = self.client.get(SIGNUP_URL, data)
76 | self.assertEqual(response.status_code, 405)
77 |
78 | response = self.client.post(SIGNUP_URL, {
79 | 'username': 'someone',
80 | 'email': 'not a correct email',
81 | 'password': 'any password'
82 | })
83 | self.assertEqual(response.status_code, 400)
84 |
85 | response = self.client.post(SIGNUP_URL, {
86 | 'username': 'someone',
87 | 'email': 'someone@jiuzhang.com',
88 | 'password': '123',
89 | })
90 | self.assertEqual(response.status_code, 400)
91 |
92 | response = self.client.post(SIGNUP_URL, {
93 | 'username': 'username is tooooooooooooooooo loooooooong',
94 | 'email': 'someone@jiuzhang.com',
95 | 'password': 'any password',
96 | })
97 | self.assertEqual(response.status_code, 400)
98 |
99 | response = self.client.post(SIGNUP_URL, data)
100 | self.assertEqual(response.status_code, 201)
101 | self.assertEqual(response.data['user']['username'], 'someone')
102 |
103 | response = self.client.get(LOGIN_STATUS_URL)
104 | self.assertEqual(response.data['has_logged_in'], True)
105 |
--------------------------------------------------------------------------------
/accounts/api/views.py:
--------------------------------------------------------------------------------
1 | from accounts.api.serializers import UserSerializer
2 | from django.contrib.auth.models import User
3 | from rest_framework import permissions
4 | from rest_framework import viewsets
5 | from rest_framework.response import Response
6 | from rest_framework.decorators import action
7 | from rest_framework.permissions import AllowAny
8 | from django.contrib.auth import (
9 | authenticate as django_authenticate,
10 | login as django_login,
11 | logout as django_logout,
12 | )
13 | from accounts.api.serializers import SignupSerializer, LoginSerializer
14 |
15 |
16 | class UserViewSet(viewsets.ReadOnlyModelViewSet):
17 | """
18 | API endpoint that allows users to be viewed or edited.
19 | """
20 | queryset = User.objects.all().order_by('-date_joined')
21 | serializer_class = UserSerializer
22 | permission_classes = (permissions.IsAuthenticated,)
23 |
24 |
25 | class AccountViewSet(viewsets.ViewSet):
26 | permission_classes = (AllowAny,)
27 | serializer_class = SignupSerializer
28 |
29 | @action(methods=['POST'], detail=False)
30 | def login(self, request):
31 | """
32 | 默认的 username 是 admin, password 也是 admin
33 | """
34 | serializer = LoginSerializer(data=request.data)
35 | if not serializer.is_valid():
36 | return Response({
37 | "success": False,
38 | "message": "Please check input",
39 | "errors": serializer.errors,
40 | }, status=400)
41 | username = serializer.validated_data['username']
42 | password = serializer.validated_data['password']
43 | user = django_authenticate(username=username, password=password)
44 | if not user or user.is_anonymous:
45 | return Response({
46 | "success": False,
47 | "message": "username and password does not match",
48 | }, status=400)
49 | django_login(request, user)
50 | return Response({
51 | "success": True,
52 | "user": UserSerializer(instance=user).data,
53 | })
54 |
55 | @action(methods=['POST'], detail=False)
56 | def logout(self, request):
57 | """
58 | 登出当前用户
59 | """
60 | django_logout(request)
61 | return Response({"success": True})
62 |
63 | @action(methods=['POST'], detail=False)
64 | def signup(self, request):
65 | """
66 | 使用 username, email, password 进行注册
67 | """
68 | serializer = SignupSerializer(data=request.data)
69 | if not serializer.is_valid():
70 | return Response({
71 | 'success': False,
72 | 'message': "Please check input",
73 | 'errors': serializer.errors,
74 | }, status=400)
75 |
76 | user = serializer.save()
77 | django_login(request, user)
78 | return Response({
79 | 'success': True,
80 | 'user': UserSerializer(user).data,
81 | }, status=201)
82 |
83 | @action(methods=['GET'], detail=False)
84 | def login_status(self, request):
85 | """
86 | 查看用户当前的登录状态和具体信息
87 | """
88 | data = {'has_logged_in': request.user.is_authenticated}
89 | if request.user.is_authenticated:
90 | data['user'] = UserSerializer(request.user).data
91 | return Response(data)
92 |
--------------------------------------------------------------------------------
/accounts/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class AccountsConfig(AppConfig):
5 | name = 'accounts'
6 |
--------------------------------------------------------------------------------
/accounts/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ninechapter-algorithm/django-twitter/958a0d14e11ab4f7f54a22f75f412d2e3ffe55f8/accounts/migrations/__init__.py
--------------------------------------------------------------------------------
/accounts/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | # Create your models here.
4 |
--------------------------------------------------------------------------------
/accounts/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/accounts/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render
2 |
3 | # Create your views here.
4 |
--------------------------------------------------------------------------------
/doc/PushModel.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ninechapter-algorithm/django-twitter/958a0d14e11ab4f7f54a22f75f412d2e3ffe55f8/doc/PushModel.jpg
--------------------------------------------------------------------------------
/doc/demo-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ninechapter-algorithm/django-twitter/958a0d14e11ab4f7f54a22f75f412d2e3ffe55f8/doc/demo-1.jpg
--------------------------------------------------------------------------------
/doc/demo-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ninechapter-algorithm/django-twitter/958a0d14e11ab4f7f54a22f75f412d2e3ffe55f8/doc/demo-2.jpg
--------------------------------------------------------------------------------
/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """Django's command-line utility for administrative tasks."""
3 | import os
4 | import sys
5 |
6 |
7 | def main():
8 | """Run administrative tasks."""
9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'twitter.settings')
10 | try:
11 | from django.core.management import execute_from_command_line
12 | except ImportError as exc:
13 | raise ImportError(
14 | "Couldn't import Django. Are you sure it's installed and "
15 | "available on your PYTHONPATH environment variable? Did you "
16 | "forget to activate a virtual environment?"
17 | ) from exc
18 | execute_from_command_line(sys.argv)
19 |
20 |
21 | if __name__ == '__main__':
22 | main()
23 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | Django==3.1.3
2 | mysqlclient==2.0.3
3 | djangorestframework==3.12.2
4 |
--------------------------------------------------------------------------------
/twitter/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ninechapter-algorithm/django-twitter/958a0d14e11ab4f7f54a22f75f412d2e3ffe55f8/twitter/__init__.py
--------------------------------------------------------------------------------
/twitter/asgi.py:
--------------------------------------------------------------------------------
1 | """
2 | ASGI config for twitter project.
3 |
4 | It exposes the ASGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.asgi import get_asgi_application
13 |
14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'twitter.settings')
15 |
16 | application = get_asgi_application()
17 |
--------------------------------------------------------------------------------
/twitter/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for twitter project.
3 |
4 | Generated by 'django-admin startproject' using Django 3.1.3.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/3.1/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/3.1/ref/settings/
11 | """
12 |
13 | from pathlib import Path
14 |
15 | # Build paths inside the project like this: BASE_DIR / 'subdir'.
16 | BASE_DIR = Path(__file__).resolve().parent.parent
17 |
18 |
19 | # Quick-start development settings - unsuitable for production
20 | # See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
21 |
22 | # SECURITY WARNING: keep the secret key used in production secret!
23 | SECRET_KEY = 'i_8$e&=cfr5bd1r(@^@gd@2y@+0@i0%ldpeke108o+pux&hsn4'
24 |
25 | # SECURITY WARNING: don't run with debug turned on in production!
26 | DEBUG = True
27 |
28 | ALLOWED_HOSTS = ['127.0.0.1', '192.168.33.10', 'localhost']
29 |
30 |
31 | # Application definition
32 |
33 | INSTALLED_APPS = [
34 | 'django.contrib.admin',
35 | 'django.contrib.auth',
36 | 'django.contrib.contenttypes',
37 | 'django.contrib.sessions',
38 | 'django.contrib.messages',
39 | 'django.contrib.staticfiles',
40 |
41 | # third party
42 | 'rest_framework',
43 |
44 | # project apps
45 | 'accounts',
46 | ]
47 |
48 | REST_FRAMEWORK = {
49 | 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
50 | 'PAGE_SIZE': 10
51 | }
52 |
53 | MIDDLEWARE = [
54 | 'django.middleware.security.SecurityMiddleware',
55 | 'django.contrib.sessions.middleware.SessionMiddleware',
56 | 'django.middleware.common.CommonMiddleware',
57 | 'django.middleware.csrf.CsrfViewMiddleware',
58 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
59 | 'django.contrib.messages.middleware.MessageMiddleware',
60 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
61 | ]
62 |
63 | ROOT_URLCONF = 'twitter.urls'
64 |
65 | TEMPLATES = [
66 | {
67 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
68 | 'DIRS': [],
69 | 'APP_DIRS': True,
70 | 'OPTIONS': {
71 | 'context_processors': [
72 | 'django.template.context_processors.debug',
73 | 'django.template.context_processors.request',
74 | 'django.contrib.auth.context_processors.auth',
75 | 'django.contrib.messages.context_processors.messages',
76 | ],
77 | },
78 | },
79 | ]
80 |
81 | WSGI_APPLICATION = 'twitter.wsgi.application'
82 |
83 |
84 | # Database
85 | # https://docs.djangoproject.com/en/3.1/ref/settings/#databases
86 |
87 | DATABASES = {
88 | 'default': {
89 | 'ENGINE': 'django.db.backends.mysql',
90 | 'NAME': 'twitter',
91 | 'HOST': '0.0.0.0',
92 | 'PORT': '3306',
93 | 'USER': 'root',
94 | 'PASSWORD': 'yourpassword',
95 | }
96 | }
97 |
98 | # Password validation
99 | # https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
100 |
101 | AUTH_PASSWORD_VALIDATORS = [
102 | {
103 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
104 | },
105 | {
106 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
107 | },
108 | {
109 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
110 | },
111 | {
112 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
113 | },
114 | ]
115 |
116 |
117 | # Internationalization
118 | # https://docs.djangoproject.com/en/3.1/topics/i18n/
119 |
120 | LANGUAGE_CODE = 'en-us'
121 |
122 | TIME_ZONE = 'UTC'
123 |
124 | USE_I18N = True
125 |
126 | USE_L10N = True
127 |
128 | USE_TZ = True
129 |
130 |
131 | # Static files (CSS, JavaScript, Images)
132 | # https://docs.djangoproject.com/en/3.1/howto/static-files/
133 |
134 | STATIC_URL = '/static/'
135 |
--------------------------------------------------------------------------------
/twitter/urls.py:
--------------------------------------------------------------------------------
1 | """twitter URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/3.1/topics/http/urls/
5 | Examples:
6 | Function views
7 | 1. Add an import: from my_app import views
8 | 2. Add a URL to urlpatterns: path('', views.home, name='home')
9 | Class-based views
10 | 1. Add an import: from other_app.views import Home
11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
12 | Including another URLconf
13 | 1. Import the include() function: from django.urls import include, path
14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
15 | """
16 | from django.contrib import admin
17 | from django.urls import include, path
18 | from rest_framework import routers
19 | from accounts.api import views
20 |
21 | router = routers.DefaultRouter()
22 | router.register(r'api/users', views.UserViewSet)
23 | router.register(r'api/accounts', views.AccountViewSet, basename='accounts')
24 |
25 | urlpatterns = [
26 | path('admin/', admin.site.urls),
27 | path('', include(router.urls)),
28 | path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
29 | ]
30 |
--------------------------------------------------------------------------------
/twitter/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for twitter project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'twitter.settings')
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------