├── .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 | ![license](https://img.shields.io/badge/%E4%B9%9D%E7%AB%A0%E7%AE%97%E6%B3%95-jiuzhangsuanfa-blue) 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 | ![](doc/PushModel.jpg) 49 | 50 | ## 演示 51 | 52 | 本项目为 Django 前后端分离项目,以后端 API 开发为主,界面基于 Django REST framework,相关演示图如下: 53 | 54 | ![](doc/demo-1.jpg) 55 | 56 |
57 | 58 | ![](doc/demo-2.jpg) 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 | ![doudou.jpg](https://s2.loli.net/2021/12/24/eoTAk8m6KfzUuQJ.jpg) 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 | --------------------------------------------------------------------------------