From 884dffa608bb1d78fc3dc24505246a065b91ea19 Mon Sep 17 00:00:00 2001 From: whatislove118 Date: Tue, 10 Aug 2021 15:55:43 +0300 Subject: [PATCH 1/8] add requirements --- requirements.txt | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f55397a --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +asgiref==3.4.1 +Django==3.2.6 +djangorestframework==3.12.4 +Pillow==8.3.1 +pytz==2021.1 +sqlparse==0.4.1 From 4985c5a6e6068e1bfa4224602f847076f985ecf2 Mon Sep 17 00:00:00 2001 From: whatislove118 Date: Thu, 12 Aug 2021 15:28:31 +0300 Subject: [PATCH 2/8] add_jwt_auth --- questions/migrations/0001_initial.py | 2 +- .../__pycache__/0001_initial.cpython-38.pyc | Bin 1349 -> 0 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 182 -> 0 bytes requirements.txt | 5 + .../__pycache__/settings.cpython-38.pyc | Bin 2601 -> 3327 bytes .../__pycache__/urls.cpython-38.pyc | Bin 1105 -> 1101 bytes stackoverflow_api/jwt_authorization.py | 87 ++++++++++++++ stackoverflow_api/settings.py | 87 +++++++++++--- stackoverflow_api/urls.py | 2 +- users/__pycache__/models.cpython-38.pyc | Bin 496 -> 3755 bytes users/migrations/0001_initial.py | 2 +- users/migrations/0002_auto_20210811_1219.py | 71 +++++++++++ .../__pycache__/0001_initial.cpython-38.pyc | Bin 2391 -> 0 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 178 -> 0 bytes users/models.py | 112 +++++++++++++++++- users/serializers.py | 34 ++++++ users/urls.py | 11 ++ users/views.py | 28 ++++- 18 files changed, 417 insertions(+), 24 deletions(-) delete mode 100644 questions/migrations/__pycache__/0001_initial.cpython-38.pyc delete mode 100644 questions/migrations/__pycache__/__init__.cpython-38.pyc create mode 100644 stackoverflow_api/jwt_authorization.py create mode 100644 users/migrations/0002_auto_20210811_1219.py delete mode 100644 users/migrations/__pycache__/0001_initial.cpython-38.pyc delete mode 100644 users/migrations/__pycache__/__init__.cpython-38.pyc create mode 100644 users/serializers.py create mode 100644 users/urls.py diff --git a/questions/migrations/0001_initial.py b/questions/migrations/0001_initial.py index 825efb2..27bba48 100644 --- a/questions/migrations/0001_initial.py +++ b/questions/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.2.5 on 2021-08-10 12:45 +# Generated by Django 3.2.6 on 2021-08-10 13:01 from django.conf import settings from django.db import migrations, models diff --git a/questions/migrations/__pycache__/0001_initial.cpython-38.pyc b/questions/migrations/__pycache__/0001_initial.cpython-38.pyc deleted file mode 100644 index c19465ac2b0f1f036b7232f6538d937750cd7792..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1349 zcmZux&2rl|5GF`Uq$o;u9OuWKP7b~4BH6h#)5&DwN_OIDZMU_2;RQnpD;gB|6N0j% zT-&SPAvyLD`YO2gK7W*x}UGTiq@eCY)lw09%O~d$ID|d&D z%1fN`8(f&dh%gco5Ta#Mn28lwi5=LLZiznZQmL9r#z4rEsrI`&Hc@$rQ+`DZ1B`$$BQO~WEU@1?nxN)^_rCMNK&g%PE_(Wvy@T2Y>#~hX z+eB?s$ANqCR_w+1<86(_?&*k8lWkp_72^H>MZCq>uA)9zMSY0c1I(<(_#FQc?mhe* zkv*>Q*nhKmbSE1}XA_yhW441#f1z5(Yg)(3{c!e;YD`3!&Xt`mgix(4rPvWr zf-=htyTz{ZXwEnYCY3YHk_1vbEchG>9a6Qpq!?7=)#Tw2|W zQsq&Ky|)m6vQO!oMJO~9Y7S3%iEI>>j=n9g!aNTr=*S?4ltDUO7N*|X*gqe?rsrn| zKhfjU(ZTo1|K!0zs`jv=`dD{(pW(Dvl{SIr^WO0~AnKIc#xfNx)-p+9UY`8xiBrzwmxTx!)D zozj!j@#~|LSIWzBSUtyIjpA#}vsymV!8z7P4z4cntxAzy!rtDq!3gBM%<{nxMHa)f zlr&!sWEoE9xL?eKu6WJ|Ys%%|_N@wsgstg`kg4ZQNi6Hthh(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o10cKeRZts93)| zBe5j2I48d>)zHvF-zBv;yClCrKcFZ-D>b>KIKH4#zqlkZIU6Wnl$MiU9-ml{sb5%{ uT3nKupI5A(o0(n&)DL3D$7kkcmc+;F6;$5hu*uC&Da}c>16leRh#3Id>oBVT diff --git a/requirements.txt b/requirements.txt index f55397a..c365563 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,11 @@ asgiref==3.4.1 Django==3.2.6 +django-ckeditor==6.1.0 +django-js-asset==1.2.2 djangorestframework==3.12.4 +djangorestframework-simplejwt==4.7.2 Pillow==8.3.1 +PyJWT==2.1.0 pytz==2021.1 sqlparse==0.4.1 +typing==3.7.4.3 diff --git a/stackoverflow_api/__pycache__/settings.cpython-38.pyc b/stackoverflow_api/__pycache__/settings.cpython-38.pyc index cf15b77a342d37d3908ed79f097e794745a0928b..786c1044676205b7faaa93be3260bc887594cf59 100644 GIT binary patch delta 1451 zcmZ`&y>l8z6lafvy8|JiFN`ExvaNh!%OY|j=R5HU7ID%63U|cOrr2C~D};rV z60HTu2uLIbGAIy%D2zZ1Mqv!%Fb)&7C>e!Gn1X3Yz!gZsOidzVFiYYv#}v3a7pNf! z!!?+P1-K43YGNJ1%_m|Fk#XoPk_mVN-sHr$<^u5c6QPdCB)@P9-XYVFA_-o=6@HTZ z%&@5a2izjFaGT6Aw0HICHLhDa5_qclbA0DWAPdcTSSHu`gA`ak^y9w9!|6jFhj+;h z7K9ZY?mkA`cn{uxj9``58HRf-0>bJ306v6|;A8j%K84TVbNGUD?}K_lv-TuGZh~M0 zQkml<%A$zk*7q5(hHLe=g&%K`QJXm~-Lb8G)@j)fN%AtLPJREN!S*`t@eAQ7_)Ry& zns?exH-_7G>wu*TEvMS*bmg?;T5i3X?n-H%zHX&Sc<3P^A9v&F?=3ss>2%JSW9Ocs zqW_Du&^z&d9w{`C99C^L!8t)gEQ0mO$&parxVw5Ug-CdfSvIrDMBX&Fs9G`erLsm; zQ!5n*LLG?Ic9>1;(9OT@JTn4sdwR;Vh`pU~eYPTCKg}S$2o6K!J zWKCyTtyY=im>w8Jd>?fm9WWBybL%8(vmKsbm$DxYPD1{>h_qjCwq53=5@cGz zs9D;?MXGB#Y-$Be5@nn#VURTI-oVS^;sbTzZ7YlcbHoQccSr}4g!@yb@# z6X+_B>7{k8NEB6H=Z!P-1rjy1^y(hVOgL}zyywsS zUwKAIEe<3~p;e`HAheu;43^8hKa(x+Q4XX7%iV3%zq5nKe@YP)Rth+)sr`TCMK$7^ zd=4dZ5HbWArMn9l?D3QujX^jDRJ@-sKZsEkJX z&y;xy2}t-`jQ4+2R=*q*OT18%n!4#ZIVb1j{?9Mx+-c?_U23UnMBw(;PwHP)LHIFi z{p2{PCO5cgif#%qzVWPAwa{XKqCm!C=`ScMg&pBpdyY1GfZPh zU=c~Akj4_0O@*php>eDl3Nkst6cIocIpnd1b(1g=8)w86=>l%i1a9LF*WJwtxOdi& zm`E2qN zVrBeEtck<#(ueUEc_a_q*~R?>badNv%5BH?U3OINd4}tiPjt_F?YPih>0JihXO3NN z@t3MU6ZL$f!7SF*UFM%2UMd{7|K!G~r_5#pt7p{>+w_mG2s<|>>^hd^*f0LBc}1w+ znDF>n&oW%r{+|&r?CqXM_ZejQ>+TDE5OA-xqyVx55 diff --git a/stackoverflow_api/__pycache__/urls.cpython-38.pyc b/stackoverflow_api/__pycache__/urls.cpython-38.pyc index b3689ea9312311ff74055801a599c492255508cd..a296348cd8e564efca467afdf278b08dd601d769 100644 GIT binary patch delta 32 mcmcb}ah8KOl$V!_0SI0Q3n%t&Jl$V!_0SH)Aza&iD$m`9_$yZpKT3nKupEo&{`2zs2j0*w) diff --git a/stackoverflow_api/jwt_authorization.py b/stackoverflow_api/jwt_authorization.py new file mode 100644 index 0000000..7b5fefe --- /dev/null +++ b/stackoverflow_api/jwt_authorization.py @@ -0,0 +1,87 @@ +from django.conf import settings +from rest_framework import exceptions, authentication +from rest_framework.authentication import BaseAuthentication +from rest_framework.exceptions import AuthenticationFailed +import jwt + +from users.models import User + + +class JWTAuthentication(BaseAuthentication): + authentication_header_prefix = 'Bearer' + + def authenticate(self, request): + """ + Метод authenticate вызывается каждый раз, независимо от того, требует + ли того эндпоинт аутентификации. 'authenticate' имеет два возможных + возвращаемых значения: + 1) None - мы возвращаем None если не хотим аутентифицироваться. + Обычно это означает, что мы значем, что аутентификация не удастся. + Примером этого является, например, случай, когда токен не включен в + заголовок. + 2) (user, token) - мы возвращаем комбинацию пользователь/токен + тогда, когда аутентификация пройдена успешно. Если ни один из + случаев не соблюден, это означает, что произошла ошибка, и мы + ничего не возвращаем. В таком случае мы просто вызовем исключение + AuthenticationFailed и позволим DRF сделать все остальное. + """ + request.user = None + + # 'auth_header' должен быть массивом с двумя элементами: + # 1) именем заголовка аутентификации (Token в нашем случае) + # 2) сам JWT, по которому мы должны пройти аутентифкацию + auth_header = authentication.get_authorization_header(request).split() + auth_header_prefix = self.authentication_header_prefix.lower() + + if not auth_header: + return None + + if len(auth_header) == 1: + # Некорректный заголовок токена, в заголовке передан один элемент + return None + + elif len(auth_header) > 2: + # Некорректный заголовок токена, какие-то лишние пробельные символы + return None + + # JWT библиотека которую мы используем, обычно некорректно обрабатывает + # тип bytes, который обычно используется стандартными библиотеками + # Python3 (HINT: использовать PyJWT). Чтобы точно решить это, нам нужно + # декодировать prefix и token. Это не самый чистый код, но это хорошее + # решение, потому что возможна ошибка, не сделай мы этого. + prefix = auth_header[0].decode('utf-8') + token = auth_header[1].decode('utf-8') + + if prefix.lower() != auth_header_prefix: + # Префикс заголовка не тот, который мы ожидали - отказ. + return None + + # К настоящему моменту есть "шанс", что аутентификация пройдет успешно. + # Мы делегируем фактическую аутентификацию учетных данных методу ниже. + return self._authenticate_credentials(request, token) + + def _authenticate_credentials(self, request, token): + """ + Попытка аутентификации с предоставленными данными. Если успешно - + вернуть пользователя и токен, иначе - сгенерировать исключение. + """ + try: + payload = jwt.decode(token, settings.SECRET_KEY) + except Exception: + msg = 'Ошибка аутентификации. Невозможно декодировать токеню' + raise exceptions.AuthenticationFailed(msg) + + try: + user = User.objects.get(pk=payload['id']) + except User.DoesNotExist: + msg = 'Пользователь соответствующий данному токену не найден.' + raise exceptions.AuthenticationFailed(msg) + + if not user.is_active: + msg = 'Данный пользователь деактивирован.' + raise exceptions.AuthenticationFailed(msg) + + return (user, token) + + + diff --git a/stackoverflow_api/settings.py b/stackoverflow_api/settings.py index 7582658..0b1f844 100644 --- a/stackoverflow_api/settings.py +++ b/stackoverflow_api/settings.py @@ -10,6 +10,7 @@ https://docs.djangoproject.com/en/3.2/ref/settings/ """ import os +from datetime import timedelta from pathlib import Path # Build paths inside the project like this: BASE_DIR / 'subdir'. @@ -35,6 +36,8 @@ 'ckeditor', 'questions.apps.QuestionsConfig', 'users', + 'rest_framework', + 'rest_framework_simplejwt', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', @@ -86,23 +89,29 @@ } } +# REST_FRAMEWORK = { +# 'DEFAULT_AUTHENTICATION_CLASSESS': [ +# 'custom_authorization.CustomAuthorization', +# 'rest_framework_simplejwt.authentication.JWTAuthentication', +# ] +# } # Password validation # https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ - { - 'NAME': 'django.contrib.users.password_validation.UserAttributeSimilarityValidator', - }, - { - 'NAME': 'django.contrib.users.password_validation.MinimumLengthValidator', - }, - { - 'NAME': 'django.contrib.users.password_validation.CommonPasswordValidator', - }, - { - 'NAME': 'django.contrib.users.password_validation.NumericPasswordValidator', - }, + # { + # 'NAME': 'django.contrib.users.password_validation.UserAttributeSimilarityValidator', + # }, + # { + # 'NAME': 'django.contrib.users.password_validation.MinimumLengthValidator', + # }, + # { + # 'NAME': 'django.contrib.users.password_validation.CommonPasswordValidator', + # }, + # { + # 'NAME': 'django.contrib.users.password_validation.NumericPasswordValidator', + # }, ] @@ -111,7 +120,7 @@ LANGUAGE_CODE = 'en-us' -TIME_ZONE = 'UTC' +TIME_ZONE = 'Europe/Moscow' USE_I18N = True @@ -123,13 +132,57 @@ # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/3.2/howto/static-files/ -STATIC_URL = '/static/' -STATICFILES_DIRS = [ - os.path.join(BASE_DIR, 'static'), -] +if DEBUG: + STATIC_URL = '/static/' + MEDIA_URL = '/media/' + + STATICFILES_DIRS = [ + os.path.join(BASE_DIR, 'static'), + ] + MEDIA_ROOT = os.path.join(BASE_DIR, 'media') +else: + STATIC_ROOT = '/var/www/static/' + + STATICFILES_DIRS = [ + Path(BASE_DIR, '/var/www/static/') + ] + MEDIA_URL = '/media/' + MEDIA_ROOT = Path(BASE_DIR, '/var/www/media/') # Default primary key field type # https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + + +# SIMPLE JWT SETTINGS +SIMPLE_JWT = { + 'ACCESS_TOKEN_LIFETIME': timedelta(seconds=10), ## вставляется в exp при генерации токенов + 'REFRESH_TOKEN_LIFETIME': timedelta(days=1), + 'ROTATE_REFRESH_TOKENS': False, # при рефрешу токенов выдает новую пару + 'BLACKLIST_AFTER_ROTATION': True, # для этого используется специальноое приложение blacklist app. ('rest_framework_simplejwt.token_blacklist' в installed_apps) + 'UPDATE_LAST_LOGIN': True, + + 'ALGORITHM': 'HS256', + 'SIGNING_KEY': SECRET_KEY, + 'VERIFYING_KEY': None, + 'AUDIENCE': None, + 'ISSUER': None, + 'JWK_URL': None, + + 'AUTH_HEADER_TYPES': ('Bearer',), + 'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION', + 'USER_ID_FIELD': 'id', + 'USER_ID_CLAIM': 'user_id', + 'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule', + + 'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',), + 'TOKEN_TYPE_CLAIM': 'token_type', + + 'JTI_CLAIM': 'jti', + + 'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp', + 'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5), + 'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1), +} diff --git a/stackoverflow_api/urls.py b/stackoverflow_api/urls.py index 5a15d96..12c5445 100644 --- a/stackoverflow_api/urls.py +++ b/stackoverflow_api/urls.py @@ -21,5 +21,5 @@ urlpatterns = [ path('admin/', admin.site.urls), path('admin-questions/', question_admin_site.urls), - path('', include('questions.urls')) + path('', include('users.urls')) ] diff --git a/users/__pycache__/models.cpython-38.pyc b/users/__pycache__/models.cpython-38.pyc index d48c8f2c7b22dac35d349e3f05c368d8b500dec6..6fbc9d21fd9263623baecda437acd3dde5aa3a23 100644 GIT binary patch literal 3755 zcma)9&2JmW72jDd$t6Wnl;kvRK9EgYpk^D82oMxS(H3zmxo}VmsV!Z$hsBCBl2+Q~ zGPBFr5*Wail%|DkB*&f#8@<$r0*YhBlt1LZpuqCF*Pik(^wR#`EU6D$ZbM>c=Y7n~ zoA=(l-|S+wTGsIU=gcea-(S_Ve-Ux~mk04ap6D-VxW<{U+4R6#z_d+8=lxPpw#%wt@F#+bT?wjoRnbMi7EIccO#7|IO~9|Lr+ca#v=9tk(Q|C!jE0B zCqHl_u^0(?$?do+LZ{l$nAN>6nWmL3{ASnO&Pr$vw}#h~z2vLmcCt6zddC`mKHN$U zK;N{IN6GH+h9aLNPlq>?Ju7(z+2i4@P8+$H~9~`AkjpfEdFs6mxh3ItR(V zg$_+gbKQshshVwHnSn(ft7UFjBgZzaH(va*W53-e#`@=sa159xXYYn%ovC~I` z-m#J=WO8QT_VDv$7h|7*i>VHhuag7pH0xx`euLd$g;> z9Yu>jzy0K~g(28(w!?=s9_tOjAfH=jM^e0C4Ca4IK7gNghkr~4$)M3x#>}@3#B%(j z^Fq&gJkf_}`i!%6Q`TdgnQgYk`Up2@3v^D=+8XP#zII3N>vtJ9ZtI^KedA89pS#PL zhMsX(qc@Tv--|J!mE46bnY9SIMe(CRJ%p3Vwm*~eh<|Fd)ow8c4!@o3BSr_w6FBV| z23|*>Vg|osN@$?}l<1~4zxg`3B(j2D6kDtAC&F^A9ylF05Y5fEUl4XI5xAajxt!x* zL`^!GWBoL*WQ}}UTG;4{IVnS#=GDovCp)1GT;JOij#9EKI7&{}jiRfe3ey+ zg}*GT@P@3RNzEe-TIHT5zX$feKWr((T31)y*o*w|6Y=i5zi7>hXgvW~%25D>IXAzDf&@ zkHWDLFVB&mW0rpIxg62%P)_K`f!wDC&-D#QS1o$S^1a;EcDK0-8)L`?CLTWk= zC9313Wye8Cdp`9mj`MNP^+%GTi11Zm7 zSQ}t<_@h2O=kX}t^|dt(MzNj;4suvk)&o_?shSa5A?bi1RCJMi_f=-1Jd=p3vBD~( z=-I5^1EDBKzsaf}27Q6DsL+7wwkmCdhSgxIVBBMJ2Fo6cu*x~)04@cN^9M3gN2BSR z74lhgB>0Y7TEtEjHV{-N|4?v*f{cQyVC=zjxUGyGA^He&)RrIN7=A{@eGf%_FWFb% zwwpX6yv}y}mD)Ry-a<8}+WlzcMu0SwGLTkA01W7IFuVzX{wrM&0J2cQI5H^%1OVX< z+((BTw*qqDOCSV>&{z_~wFJF%t9Ufr9^p7p<7oRQke4nh3SfqxVG_ExC~$oN!C>jF zBY-)a3~^IM|0kyeuet)HSnm0LcGeF=SlFCaAv_`ej)8m+vslTORP*2xx@rtYeme># zc70;%vyG-D8)V>VR@O*2S`B5a%v?D%Q{S99H1kM4-*`p7PIJ9M&70JiXwuU8Raai{ zgwK^j(()X^ZN^ZDSot6fec^T#D=nek;;!of;C?{EtFwTz1>BF7*e++amOE~2mwR2h zLQ_AjEnb`!Azr|n}%sh(t3G8Z9;mopo%=oC=e1QH7lYzC|smL zmv{kHrkGu$f%wawFBUGK`b~7D7r1i6Sr;4f$CxrT1@~fi$rqb7d^mV+YgF@E(9SRU zZf89;I>;}jJ>Mvw9+<_&i?f;20nZ*!^VGhV>gbj)2Ke^KTyFT`N_eV^DCc}U5nU@y zHq9h)8fWA$@sd=PWDbq}CRg78&34#{rMJ{X6<%#F;WI~FQ6!C)1VZU+rzvI^8%Zc8 zi=rUhaU0lL+Rr&oqFN+BqvBnSgB%} p<}cBAliJ8>drr+?WvKicf{2K&1178MbyjB;qpnwSb!+<9{{^8PPW}J@ literal 496 zcmYjNy-ve05VrGE+A0OHB3>X8O4N-3p(0R`7%C*_lErdk)1-Bh)^>o-u(I+FJO$6d z>XoUlz{EL0q9@(?KHYcs-FZ3~^buI{c}TzD{kCA&JOCqbbpnAQh6O5cg0amV;S_G- zVswp|%e+^_JdR)7#AE&)3OifqKp{54#Y|~QQ$1B&0`3$Q<3gpjylXyy5x6>rz)^x3 zN*wMkJcgNL1>W4mZ$}%?&f2~M@qQe-reDx!B6vC1nek_WmP^yA%Y0q)N!T%6LP}b2 zLQJ0!5U+*J147m{6>UYjFv|2UGb+b97tHvgn&;&QlKW6-!p}`?MOFNiQJpJMJ@Vo3 zG``|$sjF3dE2{-hwIZuctTauRP?ry)dLnd{$92=$9v-bW(thh6xN;zLaf2tJZ;sf4 wmh&n~t5VB+7SUQ~QQIah)%5-dY}TyYH2&|{W(StoJ!>yL$5zJb1vv130adSk(*OVf diff --git a/users/migrations/0001_initial.py b/users/migrations/0001_initial.py index 355b468..7271e23 100644 --- a/users/migrations/0001_initial.py +++ b/users/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.2.5 on 2021-08-10 12:44 +# Generated by Django 3.2.6 on 2021-08-10 13:01 import django.contrib.auth.models import django.contrib.auth.validators diff --git a/users/migrations/0002_auto_20210811_1219.py b/users/migrations/0002_auto_20210811_1219.py new file mode 100644 index 0000000..e090aed --- /dev/null +++ b/users/migrations/0002_auto_20210811_1219.py @@ -0,0 +1,71 @@ +# Generated by Django 3.2.6 on 2021-08-11 09:19 + +import datetime +from django.db import migrations, models +from django.utils.timezone import utc + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0001_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='user', + options={}, + ), + migrations.AlterModelManagers( + name='user', + managers=[ + ], + ), + migrations.RemoveField( + model_name='user', + name='date_joined', + ), + migrations.RemoveField( + model_name='user', + name='first_name', + ), + migrations.RemoveField( + model_name='user', + name='last_name', + ), + migrations.RemoveField( + model_name='user', + name='login', + ), + migrations.AddField( + model_name='user', + name='created_at', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2021, 8, 11, 9, 19, 42, 187727, tzinfo=utc)), + preserve_default=False, + ), + migrations.AddField( + model_name='user', + name='updated_at', + field=models.DateTimeField(auto_now=True), + ), + migrations.AlterField( + model_name='user', + name='email', + field=models.EmailField(db_index=True, max_length=254, unique=True), + ), + migrations.AlterField( + model_name='user', + name='is_active', + field=models.BooleanField(default=True), + ), + migrations.AlterField( + model_name='user', + name='is_staff', + field=models.BooleanField(default=False), + ), + migrations.AlterField( + model_name='user', + name='username', + field=models.CharField(db_index=True, max_length=255, unique=True), + ), + ] diff --git a/users/migrations/__pycache__/0001_initial.cpython-38.pyc b/users/migrations/__pycache__/0001_initial.cpython-38.pyc deleted file mode 100644 index f694720262455ec4b61c360fcdb4a1b16335a900..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2391 zcmZuzOOqQl5cZ76&-FgDyII0Z5CSnt#=C()LP07o5~w1n6xroq4pZ_-w&j&Ij-=U* z=R^V5y>jA^9QhIa5nVa$l_OOUT9Wrck(nA<-D-79-`6d9XMVm}g6DU=f`6PVmHx28 z=_dl=7JTX^G^9iVUg`$!H*~)tFOQ>cG_G_jfgi7Sg9jy2CehC&647W>`>EWmk>E+G zT{#3FrAO`1G{dNTMXq9|GF1oe1LE8Xd>*PZxUjlgzjx2Fd6wJ&c0MzWhexbz z14j|g^{{(kb93uzg1M$L$rUtxCY4SE9@AuupCz1%fgYM@2gXdu$bA!jeXkvv7R=6) zR8p)dF>@2i##m01T{<-lFlCstf|}MImAy<+=g-t9SgHL?lEWW>cb6u|5o8NgrpYk` z=h=XX!u65sx67u|<5=vPNaUP*+ZI!a$|qFXWfza{QN;!VR-=#}Vhs&b$PS?)RtR&B zz;w)%vPp^dnI2}jM)cW)vy^E*MX)w-z{CKi(s5iY9Xm%#W1TBlMUBut&bc;?A?1@q z(`VW*rfwry&`dQK>}gJm_H7^CM)E5>&n zQy@g4Ork#Br!q$0P;E7r5n%(SVMBtTTb;ObsdJ^1piJ;-eE5q+wQ3fql$lJ%R4F{5 zO0I!gF52d-!*~z4Ay{Wp%|Jh)ylA>UvO8&)wav^DyJUG{7Z* z!}e%FU+MFRbp`x#f_fBbpQJ!A@-t(!BNXK{on;9VN&_J%>mxX+sJ1zP(=^Kkuw$xz zl9>a{Xfn!}prmjD@3RDri)!@lo#>9Vs&c_s#sv{{Uf zQwFkiK=uE!7S2=w{PaDGnD zFcs?=s>;VXbNZ-ACOnszn>y|(ZHvLwvfc;?Q*iV|zRvoLa-v{h;_sRI7z@aEsUEdA zOg-^OSYnz<0vAxuX_A;$l03~ZxAQ@&F~Kw(RHjAfgbG4M%Aj0A3((wMmoyLU)g{Xg zvO8>W8_vA@Hmqsfg(CIc`FnumHXBnfw(evZr&xHYN=8@=GE+-4p(X3dip`XB*5SsE zU@7?DnVlTp9hIeNerYSpJE-KEai}B>0g6meQx@62S$v4abUU-HH=DCwK(qjuW(IWm z+xNGUd|)Hm=CAKN)=G8u;e2F@XM1#O>v{*OXjf;G&SRN5gp jI!NOqL|x1ilzi_i2x97=`^>p%6F}ANzJfZeyk7nnGpzg# diff --git a/users/migrations/__pycache__/__init__.cpython-38.pyc b/users/migrations/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 2c6608e600afa06a09bff94c42eae8f8b2c19ad6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 178 zcmWIL<>g`kf=hb@6G8N25P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;x_`erR!OQL%n` zMq){3aZY|&s-dBUzDsIxc1eDLen3%vR%&udaeP6gesM`+ayC%DC@m+yJU+1?Q@<3f qGdDB62&gwduUJ1mJ~J<~BtBlRpz;=nO>TZlX-=vg$imM+%m4r;3@;o2 diff --git a/users/models.py b/users/models.py index dd4e152..f192427 100644 --- a/users/models.py +++ b/users/models.py @@ -1,9 +1,115 @@ -from django.contrib.auth.models import AbstractUser +import uuid +from datetime import datetime, timedelta + +from django.contrib.auth.base_user import BaseUserManager, AbstractBaseUser +from django.contrib.auth.models import AbstractUser, PermissionsMixin from django.db import models # Create your models here. +from rest_framework_simplejwt.tokens import AccessToken, RefreshToken + +from stackoverflow_api import settings + + +class UserManager(BaseUserManager): + """ + Django требует, чтобы кастомные пользователи определяли свой собственный + класс Manager. Унаследовавшись от BaseUserManager, мы получаем много того + же самого кода, который Django использовал для создания User (для демонстрации). + """ + + def create_user(self, username, email, password=None): + """ Создает и возвращает пользователя с имэйлом, паролем и именем. """ + if username is None: + raise TypeError('Users must have a username.') + # + if email is None: + raise TypeError('Users must have an email address.') + + user = self.model(username=username, email=self.normalize_email(email)) + user.set_password(password) + user.save() + return user + + def create_superuser(self, username, email, password): + """ Создает и возввращет пользователя с привилегиями суперадмина. """ + if password is None: + raise TypeError('Superusers must have a password.') + user = self.create_user(username, email, password) + user.is_superuser = True + user.is_staff = True + user.save() + + return user + + +class User(AbstractBaseUser, PermissionsMixin): + # Каждому пользователю нужен понятный человеку уникальный идентификатор, + # который мы можем использовать для предоставления User в пользовательском + # интерфейсе. Мы так же проиндексируем этот столбец в базе данных для + # повышения скорости поиска в дальнейшем. + username = models.CharField(db_index=True, max_length=255, unique=True) + + # Так же мы нуждаемся в поле, с помощью которого будем иметь возможность + # связаться с пользователем и идентифицировать его при входе в систему. + # Поскольку адрес почты нам нужен в любом случае, мы также будем + # использовать его для входы в систему, так как это наиболее + # распространенная форма учетных данных на данный момент (ну еще телефон). + email = models.EmailField(db_index=True, unique=True) + + # Когда пользователь более не желает пользоваться нашей системой, он может + # захотеть удалить свой аккаунт. Для нас это проблема, так как собираемые + # нами данные очень ценны, и мы не хотим их удалять :) Мы просто предложим + # пользователям способ деактивировать учетку вместо ее полного удаления. + # Таким образом, они не будут отображаться на сайте, но мы все еще сможем + # далее анализировать информацию. + is_active = models.BooleanField(default=True) + + # Этот флаг определяет, кто может войти в административную часть нашего + # сайта. Для большинства пользователей это флаг будет ложным. + is_staff = models.BooleanField(default=False) + + # Временная метка создания объекта. + created_at = models.DateTimeField(auto_now_add=True) + + # Временная метка показывающая время последнего обновления объекта. + updated_at = models.DateTimeField(auto_now=True) + + # Дополнительный поля, необходимые Django + # при указании кастомной модели пользователя. + + # Свойство USERNAME_FIELD сообщает нам, какое поле мы будем использовать + # для входа в систему. + USERNAME_FIELD = 'username' + REQUIRED_FIELDS = ['email', 'password'] + + # Сообщает Django, что определенный выше класс UserManager + # должен управлять объектами этого типа. + objects = UserManager() + + def __str__(self): + """ Строковое представление модели (отображается в консоли) """ + return self.email + + def get_full_name(self): + """ + Этот метод требуется Django для таких вещей, как обработка электронной + почты. Обычно это имя фамилия пользователя, но поскольку мы не + используем их, будем возвращать username. + """ + return self.username + + def get_short_name(self): + """ Аналогично методу get_full_name(). """ + return self.username + + +class UserProfile(models.Model): + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + logo = models.ImageField(blank=False, null=False, default='media/default/') + + + -class User(AbstractUser): - login = models.CharField(max_length=150, blank=True, unique=False) diff --git a/users/serializers.py b/users/serializers.py new file mode 100644 index 0000000..6140e64 --- /dev/null +++ b/users/serializers.py @@ -0,0 +1,34 @@ +from django.contrib.auth import get_user_model +from rest_framework import serializers +from rest_framework_simplejwt.serializers import TokenObtainPairSerializer +from rest_framework_simplejwt.views import TokenObtainPairView + + +class CreateUserSerializer(serializers.ModelSerializer): + class Meta: + model = get_user_model() + fields = ['username', 'password', 'email'] + #extra_kwargs = {'email': {'required': True}} + # validators = [ + # UniqueTogetherValidator( + # queryset=Event.objects.all(), + # fields=['room_number', 'date'] + # ) + # ] + + # def validate(self, data): + # print(data) + # # тут можно написать свою валидацию + # return data +# +# class MyTokenObtainPairSerializer(TokenObtainPairSerializer): +# @classmethod +# def get_token(cls, user): +# token = super().get_token(user) +# # Add custom claims +# token['username'] = user.name +# # ... +# print(token) +# return token + + diff --git a/users/urls.py b/users/urls.py new file mode 100644 index 0000000..f3f31ee --- /dev/null +++ b/users/urls.py @@ -0,0 +1,11 @@ +from django.urls import path, include +from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView + +from .views import GetUserAPIView, CreateUserAPIView + +urlpatterns = [ + path('hello/', GetUserAPIView.as_view(), name='hello'), + path('auth/', TokenObtainPairView.as_view(), name='auth'), + path('refresh/', TokenRefreshView.as_view(), name='auth'), + path('register/', CreateUserAPIView.as_view(), name='reg') +] \ No newline at end of file diff --git a/users/views.py b/users/views.py index 91ea44a..c2feb29 100644 --- a/users/views.py +++ b/users/views.py @@ -1,3 +1,29 @@ +from django.contrib.auth import get_user_model from django.shortcuts import render - +from rest_framework import generics # Create your views here. +from rest_framework.permissions import AllowAny, IsAuthenticated +from rest_framework.response import Response +from rest_framework_simplejwt.authentication import JWTAuthentication + +from rest_framework_simplejwt.views import TokenObtainPairView +from users.serializers import CreateUserSerializer + + +class GetUserAPIView(generics.RetrieveAPIView): + permission_classes = [IsAuthenticated, ] + authentication_classes = [JWTAuthentication, ] + + def get(self, request, *args, **kwargs): + return Response('1') + +## чтобы уж совсем не быть ленивым, я напишу свою регистрацию + +class CreateUserAPIView(generics.CreateAPIView): + permission_classes = [AllowAny, ] + model = get_user_model() + serializer_class = CreateUserSerializer + + + + From d747c41a03c802567d4c4cb7b0176ba5cd2601ab Mon Sep 17 00:00:00 2001 From: whatislove118 Date: Sat, 14 Aug 2021 05:42:28 +0300 Subject: [PATCH 3/8] need_fixes --- .../__pycache__/settings.cpython-38.pyc | Bin 3327 -> 3594 bytes stackoverflow_api/settings.py | 8 +++- users/__pycache__/models.cpython-38.pyc | Bin 3755 -> 4914 bytes users/migrations/0003_auto_20210812_1631.py | 29 ++++++++++++++ users/migrations/0004_alter_user_id.py | 19 +++++++++ users/models.py | 36 +++++++++++++++++- users/serializers.py | 29 ++++++++++---- users/views.py | 24 +++++++++++- 8 files changed, 134 insertions(+), 11 deletions(-) create mode 100644 users/migrations/0003_auto_20210812_1631.py create mode 100644 users/migrations/0004_alter_user_id.py diff --git a/stackoverflow_api/__pycache__/settings.cpython-38.pyc b/stackoverflow_api/__pycache__/settings.cpython-38.pyc index 786c1044676205b7faaa93be3260bc887594cf59..0eca91996a5276feb45121585b809aae7a6dd52a 100644 GIT binary patch delta 591 zcmZvWTW=C!5Qg_#XbF^}m2;u=e6FnLL#+o|4id36u%yW(W~F^Uw`(ahutcS4deJ+R z8p*~xF6?h0@xs60mH)&T?{PtzXyUw?%s1~dGx<8f_hDCIc-SGBkzMf@e|Vd&Fsn9~ zU!1_LI>`kOyzs$~0D>4*ZFC7Cx{NTlV=N@741 zNvpM+mW~C|0f$zU>NQ@iN#L)F^^z`?DrzNqSXLXpKlH*sI_@E<;oZ~q!`iAOshYl5 z--+&44y2>whVR1G-)Oqdt9{@2U|VZ{v%MABd^_%_vX>N$Cz7#3Mpg>>L{8btWs~Vl zLdj%PSqf|v@^be1e~3GkmY){Zl}vi`nX(m=y^?6uc*i3iU}q&F3 zAZ%gpAMg*@+KXT*`WZx=;qjPx?+tTfe8v4onx;yko&w>s_fW@g&}pZ1(zJZ+M-U-| z5kV9&#H|4yMuG#-%|RHtWDz__B84Y!jBmn;tBiD&*f@!7A2J#X2^yi7nKGcN;tZGP`Ow!a2x? yQen@zCq;Iil~2X4c&oJUTzIc!$~&)WgY3tlTY9h$w4(Ci3fR}$F}dv*t^5s+0b0rc diff --git a/stackoverflow_api/settings.py b/stackoverflow_api/settings.py index 0b1f844..c1a44c5 100644 --- a/stackoverflow_api/settings.py +++ b/stackoverflow_api/settings.py @@ -38,6 +38,7 @@ 'users', 'rest_framework', 'rest_framework_simplejwt', + 'rest_framework_simplejwt.token_blacklist', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', @@ -150,6 +151,11 @@ MEDIA_URL = '/media/' MEDIA_ROOT = Path(BASE_DIR, '/var/www/media/') + +DEFAULT_USER_PROFILE_LOGO = '/media/users/default/logo.png' +CUSTOM_USER_PROFILE_LOGO = 'media/default/{}/logo/' +GITHUB_LINK_PATTERN = 'https://github.com/{}' +TWITTER_LINK_PATTERN = 'https://twitter.com/{}' # Default primary key field type # https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field @@ -160,7 +166,7 @@ SIMPLE_JWT = { 'ACCESS_TOKEN_LIFETIME': timedelta(seconds=10), ## вставляется в exp при генерации токенов 'REFRESH_TOKEN_LIFETIME': timedelta(days=1), - 'ROTATE_REFRESH_TOKENS': False, # при рефрешу токенов выдает новую пару + 'ROTATE_REFRESH_TOKENS': True, # при рефрешу токенов выдает новую пару 'BLACKLIST_AFTER_ROTATION': True, # для этого используется специальноое приложение blacklist app. ('rest_framework_simplejwt.token_blacklist' в installed_apps) 'UPDATE_LAST_LOGIN': True, diff --git a/users/__pycache__/models.cpython-38.pyc b/users/__pycache__/models.cpython-38.pyc index 6fbc9d21fd9263623baecda437acd3dde5aa3a23..bc9ee632e4a27ee0b86d4dfab61f49d77f5a7c3e 100644 GIT binary patch delta 2112 zcmZvd%X8aA9LKGPZP}I|ahyj>Lq(wvLmyB|3j+g9)3}sLlTKq_G{b0=>^e~@Tj^S& zlo@K8w8McGrZocxE@g&GV3?D(Q4Gg6pGK2h6#Vo0%QL&o^KDe)v>`WJvaEgk))QA-^o?1tLsGin%+FZ%e{T z%aY3@8H{3Cm_S)H8;y$&BZ2HSC|ilJp?iURO*oe0l5H@$YBh|8w@4Wm1PYH_vsz_& zuIT4xsB4B>-^D)P&WEuVy2;+|1M#E6Ppvg|S5qJ|5lHlgq$jAfkc2Og*bPxnAyVPT zZwPui^u^H6z|SJDAfMat6Y%>;QqPkVQRhUxpB9!Q`T$9j%+-iKNU|h{c8K(mJX&6v zeo{bX3d`bQeH$4-Sq1~Sg6}GqR-_=c$gG;lE*aw-rO*TDnlDeL*TaIJ^A zNai!tA{f;W5i0^ajApX9y?3_Ohw0t#CdTGXR66C=qFFAxtc;65Q4H*P7>9MDrg&{Xm0!RrBw*ulUWi_B9Rds{coO3A$_L z5_<_#7o{LNW1IE!fm{cDLBgpUgxb`lz~MLqY!oI)c#F1U5|9vXh9{51p-%;DFPH9y zc|u?&i^ILEk4%&8Ux;%Bps7#NaGtQoyDxIDfHzuY|O$D*4!)m4*Y&$t#8m&x}jY{)*jV{~H zoWqWV53-$E)3uy>kSNnn-S8Fks5#?!ZV+`X*QQ%?_5sFTz^}niNJW%|yigGHg8Xkx z%n3Px@%~NTn(#YU7dVFadEjUad`RFT4&>wxav?EMoR~@E6Wu8FOtcAN3@2ppyINqk)oj|DM0TUu%CE`ghu2ae(go zYyLw3wA8+?H2})2YcOh?E(LTUm74z>DC6!r`*UP1%#CG9n9fD54 zD~dfd6_J=M_C3c?4?-nAme)6kkBv@`jh-CWRS&zySnO<@K&hOY=&Yl9ky152~ksf!za=O<~$5 z-)rGPtOznF$q2YbRZxYnyEwp(w0=#moz)-?F41IBi2qqw=U|-3lrs9undu0hO|#szkFf3(I9DX{b|2;{;R? ztq^6|MKuc$Y*@Bp1r?tG5@I*s(2w9=Q(YKjbNSqWI1?`9&kXc&)G0ZP0KBK$^WMOmHL;(6Gl9$a#7Xk>^gZLo_72Q2B_LD@RHi-G zDWNJ1ZC49&5W{id)pymvfXD=%+a*CEQb))WQ2Oqld9C4U}{A)%-PZNc;JfwK9L4xt!NPU7sXq%?wquX zkHECpF3gBOW^WoBctCg+*PZeRp)^8h5)Z?thwZ{=GMg2c8KSe>tRJ=paU5<$T^5SD z(w6U|ZkX6Xak`f9ex`1V-=(SROa3T7iz_A(>If!)EIe3?_+p2}l;iHQ_^2Co;(rN8 z{Icp~TpU@!)mdCIhcJ)u-$%x3n41DvfRW<8nqteY<+I$gy~;SbB3>95wpF-69qvX8 z3N#OmHDn1JyUdeTCuS>)z9vgO-swhsGhAnzd>SfAlhIB;T8-H&rwcS%IBxm^sB^1v zw6QMr4d_j#Uh<2>eKwnohvJh}yI*W{qc+PN26~*dlc8p=!Ok(7(a{X?2*->+!f%Po t-j4=)mAS_WWpOq!tcEmicN02@* Date: Sat, 14 Aug 2021 05:46:24 +0300 Subject: [PATCH 4/8] sm_fx --- .../__pycache__/settings.cpython-38.pyc | Bin 3327 -> 3327 bytes users/__pycache__/models.cpython-38.pyc | Bin 3755 -> 3755 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/stackoverflow_api/__pycache__/settings.cpython-38.pyc b/stackoverflow_api/__pycache__/settings.cpython-38.pyc index 786c1044676205b7faaa93be3260bc887594cf59..9f77e64b39655ddf131caad686eb1a0de225ebe2 100644 GIT binary patch delta 20 acmew_`CpPdl$V!_0SK Date: Sat, 14 Aug 2021 06:06:39 +0300 Subject: [PATCH 5/8] fix_bug --- .../__pycache__/0001_initial.cpython-38.pyc | Bin 0 -> 1349 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 182 bytes .../jwt_authorization.cpython-38.pyc | Bin 0 -> 3475 bytes .../__pycache__/settings.cpython-38.pyc | Bin 3594 -> 3599 bytes stackoverflow_api/settings.py | 2 +- users/__pycache__/models.cpython-38.pyc | Bin 4914 -> 4869 bytes users/__pycache__/serializers.cpython-38.pyc | Bin 0 -> 1537 bytes users/__pycache__/urls.cpython-38.pyc | Bin 0 -> 561 bytes users/__pycache__/views.cpython-38.pyc | Bin 0 -> 1727 bytes users/migrations/0005_auto_20210814_0546.py | 36 ++++++++++++++++++ users/migrations/0006_alter_user_id.py | 18 +++++++++ .../__pycache__/0001_initial.cpython-38.pyc | Bin 0 -> 2391 bytes .../0002_auto_20210811_1219.cpython-38.pyc | Bin 0 -> 1391 bytes .../0003_auto_20210812_1631.cpython-38.pyc | Bin 0 -> 1188 bytes .../0004_alter_user_id.cpython-38.pyc | Bin 0 -> 668 bytes .../0005_auto_20210814_0546.cpython-38.pyc | Bin 0 -> 1206 bytes .../0006_alter_user_id.cpython-38.pyc | Bin 0 -> 654 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 178 bytes users/models.py | 2 +- users/serializers.py | 18 +++++++-- users/views.py | 8 ++-- 21 files changed, 74 insertions(+), 10 deletions(-) create mode 100644 questions/migrations/__pycache__/0001_initial.cpython-38.pyc create mode 100644 questions/migrations/__pycache__/__init__.cpython-38.pyc create mode 100644 stackoverflow_api/__pycache__/jwt_authorization.cpython-38.pyc create mode 100644 users/__pycache__/serializers.cpython-38.pyc create mode 100644 users/__pycache__/urls.cpython-38.pyc create mode 100644 users/__pycache__/views.cpython-38.pyc create mode 100644 users/migrations/0005_auto_20210814_0546.py create mode 100644 users/migrations/0006_alter_user_id.py create mode 100644 users/migrations/__pycache__/0001_initial.cpython-38.pyc create mode 100644 users/migrations/__pycache__/0002_auto_20210811_1219.cpython-38.pyc create mode 100644 users/migrations/__pycache__/0003_auto_20210812_1631.cpython-38.pyc create mode 100644 users/migrations/__pycache__/0004_alter_user_id.cpython-38.pyc create mode 100644 users/migrations/__pycache__/0005_auto_20210814_0546.cpython-38.pyc create mode 100644 users/migrations/__pycache__/0006_alter_user_id.cpython-38.pyc create mode 100644 users/migrations/__pycache__/__init__.cpython-38.pyc diff --git a/questions/migrations/__pycache__/0001_initial.cpython-38.pyc b/questions/migrations/__pycache__/0001_initial.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..af6ceae16e9824b94b654f24fc68120d0a0785af GIT binary patch literal 1349 zcmZux&2rl|5GF`Uq9{ss9OuWKP7b~4BHFn$)9G~LN_OIDZMU_2;RQnpD;gB|6N0j> zT-&SPA-T1W&{x5=C*ON%4s92t6g$&`1I+GXvESm`1<$)(*TC`X^;YnZQmL9r#z4rEsrI`+Hc@$rQ+`1V1B`$$BQO~WEU@1-pc%K` zlZXTk*m0Yg%mO#=FoGXu!J0Sr%w#RJdCXz$8-kh(?z`@L1Emhyd+6y^_AY80tj9Jg zZ4SVcDpuc?4EDlb%rft(gR zuTVUOG1sBdv4%>sD;hGU8kZcd>ZuONyap3!$7ht9!rQT1#@UmQMOR%AwW0 zC{-?{*n0~BD0`H?S%g9(q1NIlFOiMH($Tl&RhZ}D1RWXVkTOW8%fi%K8~f+u*Yy1C z;3s-~Iy(4Xc^^GENYxouR3GaOFZ7F2-nTs3$08i+%c%D7A}m(o7_Q4&?2eGiF;AdY zy`!{*2v&aXVO9W-((kZlO%{h~lqr))jgepAGj%2Z)tr0>X3^^)!WXhD)uw zqf>fvI(~h0@=Cc`4y)(*n^Am;c~;8@>YrnMqGRyrRiY$g{ zDQUj+Wf@NAxL?eKu6WMOAq zadGy@vk&IqcBi)0mLB<>Jo^i+e^%%F4mM)N!(f`Fv$`(rm4!{J64u=Bd^8T~f6idd iS>fwD>um0BUrR60rktW9 literal 0 HcmV?d00001 diff --git a/questions/migrations/__pycache__/__init__.cpython-38.pyc b/questions/migrations/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..06255acb8c312fd30bfa644a3de73964058a6fbf GIT binary patch literal 182 zcmWIL<>g`kf)y1)i6Hthh(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o10cKeRZts93)| zBe5j2I48d>)zHvF-zBv;yClCrKcFZ-D>b>KIKH4#zqlkZIU6Wnl$MiU9-ml{sb5%{ uT3nKupI5A(o0(n&)DL3D$7kkcmc+;F6;$5hu*uC&Da}c>16leRh#3H|6)xpth-m;EhZ2$XlA$#7z_TFU-E~D^L9kJn@}bJ6<^UXQmS)Usl z%SyPsu>;oJ3zGB?b&{8c&I#Q9*Lc{HEjv<8F3K{s6-TM5MYX0CHPKfcy_PDbL|b#x zwM;Q1+PX7R9KrmQldX*wM`h_#$xhpu>yn*esU@|Tv-LAle&jJcNO?KR_$&y@^$UJp ziL&fUi8X?E)U zaQollVaqMqlmdB0Y00bdin=OYQ(96*ThY-{TZ;dNEuWEsR7+WsZKbMs?trR}}zHHQ&%BDQomD{u^(Hi@eM4 z8wpyO!Pmo^d;|A7U!xh}LU_mEo9O(_?_>CZ5q^ix#$khR^ETf=d!2W}1>WIXyle1o zxM&Ew_jva(TA1DDcf%#JHE{lrcXnnO;cb|@&mRFyhi_v&UkjIDqfH&yzK&r42!Fu; zPQiFHiT6!IIBt`914e)vTY#kvf&-f#dkqI_)9+iFU}hXo7+H+Zeh*PT}&- zdpk>rucLWm#;r5sh(Xi)4u-76698j@)8>dABmAD|Ab@*piM9h6U4jgnZV}OiA+P)o z5O))pwn1!on^*<+%r?N>WD{wA>xitU~r~L$oKLL`?wk^PJ z^?-03uMyq>_=kA*;zY#68hMuq)F!4b$7cYJ zhq<6YgHMo4jCJ|uFwA3l+wXRaVbG^QZ)hEq)jKqC=;HST;obJq&Mw-`<%3 z8~Rg3FIE8P5F#-wwlcIW*n3Ls65B*gmp=dj6lWk|3H&?)e?QV87YzREAbt=;$WFpY zF}^YEe86g(ulG)I0rT$?g|T^X|9>$P!+~+c>5rfmxkHT(%pl>gFJM4i4Y>x;jyNNV z0Cd`WrUSO}Gad0-%ylvhut3DBa85x-c7*Bx|2l|4wveDCq5+87Lm-CrH&%FomPBMj z1PHPamQJ2NMZSq|Y>)Dcd@T^_uJFA^XH4Yt_8_8W9?KBy$0X+-wdVC^Fni>9ek9T; z$UM^Z=+Ffgn50FnSDqIY$-Kxcn|YD$H=J@1y_^s#)T`cGv*EGX@|8$;+&Rou9afK0 zHY>R{izbD=UWsF7$zwKkEXU8MBF$&cY?Sucr6%)(Xq2q?Y!)XHoxX!q-;v0Oza(!G zsQGcihYD=+Wa-Cz)Rj+{V-wo?Bt1z&9ILr~JSwRq@`N1ya*$l=m2^ z%soLtp>Z|JCHBq6;M9K^4{2XYAD6R=CTmJg5qElA9;dc8|LU%qT1XlxsbrvcQDM#C z7KL>gjroZ{T9I2ypj6aV<(hh4s=ua5fmY3W@5mDU*(jBPzLIKbtMa1!l~h;0l;#fx z=}M+D(n6h|lx+33d`-v7*Re8P$zpAzw^rM|wxw4_{U2;S`KG0}l-an@;;Sc7X#YfP zJ%WxeQcmqr2xvI2ZHUxZBH9Spet1G*g;=1FL4}c!5KqKwubYsJVJ$I^>?8~&6?A8t zgo)yxa%8vAL7jRvS!f+4Kir6t2r)&<+!HX!(oo&Pyxqw3|#x1?|<$L=g#>Bc*KPBTs2mo3rGTc;CIvTeOuf*j*H~P{l(F)GXtB zpo7nwKEIf>6)q(`EcN6Ltn?+k(ENy8{jzf;Sid*D;GH%BJ%qmweD7?8Zx`~pNJT1n z6ENnH>O|Qy)1RE4K5KqH{Z%wF-TwymC=WbJ0546m((X4RT_Y7*6y;93%%5?C=__UE z!${dTtx{0F%xk3(Wz!@BCYmK~`z|ALpJaT164@glYGQ$YFyF^weR z#aD*kBvl8J?cs}ADo#EzXYi9GK8jZ{Dauh{@C(=wb9`|+0?uC8qs;s9zCWPa;uHNj MaGJwkM#-uF0p8CY#sB~S literal 0 HcmV?d00001 diff --git a/stackoverflow_api/__pycache__/settings.cpython-38.pyc b/stackoverflow_api/__pycache__/settings.cpython-38.pyc index 0eca91996a5276feb45121585b809aae7a6dd52a..d5d6230b8c2835f43c758f4144a8149b555adb72 100644 GIT binary patch delta 374 zcmYjNIZpy%5Z!OVMPNVfdzbrA4i(p1LA{YmFnoVz4pH6wcSHQ-5hKd834a9E5~%oB$bF1VqB!vimT z@FRdALY7J%gb_g$F~pI8W;w_UoqR}|PNX!+Vo;Dq23h2gx9n|(!pJV>`cb3+N>~uQ zMNL9^B)1s_Er~*?P#9I}MxansL`+27ba%c{qXd>nGuf^FI$f|lmc^*lU%xVzDSekh zoigT}!0WvkKGRJ5)t?*4QDuN>JJBnLrVQi)|`EG8ou;2ltpfe$cP>P8pc9uMo zz3~bBHO4q6lgY`MGc%dT#6!Zmwk)rqC z8!Yt9x{{FI7^WZ*79~p4Eiv3OsdLI6YkX3aX-X2e)otP>sf|m0Q;$mh@#VfL>AMtV z$=JhMFSma5AI~>mX=$B`%&;o6tVx!2$+01Mdx%Z8+Dg}LcG}-ZYwWUXdoh2p$36!f wQsszaYMkiiQ_lXG&U;ar!@M#I7lT*zqz1L2XOFw0-`c2j-+lMW)x~=I0_byG-~a#s diff --git a/stackoverflow_api/settings.py b/stackoverflow_api/settings.py index c1a44c5..db5fe82 100644 --- a/stackoverflow_api/settings.py +++ b/stackoverflow_api/settings.py @@ -26,7 +26,7 @@ # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True -ALLOWED_HOSTS = [] +ALLOWED_HOSTS = ['*'] AUTH_USER_MODEL = 'users.User' diff --git a/users/__pycache__/models.cpython-38.pyc b/users/__pycache__/models.cpython-38.pyc index bc9ee632e4a27ee0b86d4dfab61f49d77f5a7c3e..0570333fa55d8b93d7df23a838eacb39f8ac63ca 100644 GIT binary patch delta 486 zcmYk0J5Ss|6oozZV$ZHU>$MjX31t(WD@;H^kv6^VFs z+i+><=prbPfL}ltDMkA2Dd;JvQ0}Y{VkDpW&bh}|b6Y+xi+Le(8lB%mAL7*;v1%Qh zl1ntnK$Ci6tY?6MPM=XXWD9wdm_eqlON$&QZmBbwZL-0ieox@q)7j)4h z(;kvb*03O1j(4lI>APS2L}wv#`@;O93L#$Gf2|?HC~U)@P8;4jXT^Rs8c;H*M2+#E z8{n46n(bt`b$1}j>{Dno?45LNGfwvxHcwJjEwuvN0 z4^(fxFyYR@3rFSR)p*c^FF@8$zz6VZe5NsxIsBgA|9PI-?0l$xsj>!Ru7PLaMmb*l z%+~GqpD+o76EHXx=4J+n$ZlFd!R(vd#*PC7unSqtIm|9P0jp1W9#a9`?i0=iP6u8@ z_pt9^zx2;@=svhSfIJk|C?68xuEERDU{LHCyaFDSu$}`S0<6PN4H$y3XF#}1`3RJk zji{1l8n1L`E9xJDwaFVEwPi@AXejU(n%cw8=ZJ15h`>hN>zNBoIo5Bc?HYS zbLS%^lj_7hux{zuqwx>`Rqw{L1P*JDF3rZCBOE>lSw#FTeDdn`D^1w$ei-3K zU9VnK--}m6c~ASa#*D@+LYdwha$mjm9+H~+>Rm)WV`J*K_haMIoPIWzGeMF8cg1Rm2)QKFUJHk!9$TPYt{4|IHqkAGuOHqkfoJ_5#{Lx}Q3+^ozaIasPI#Cro zAuEBWH22h4`P^z&e#85$CeigPp2uUwWt?V=iwB}>DIM^5M3uVDB!iF&oF-Hx6D}2) z+ZV&+dd$CMaI?qdS2$rb&m=}(K-o-*4_c3f0g2=7!WJ;hz zWb&ijn6FP%^%@4Gf$iX|ae3FkBwFQV%;AVin$DsQO&D^fk#!r?gsuW-X&i#T2e<$R zrKuzvVl2>Td3Sk3I*=ID;VLe=atn-AALRy^`XwZX)LW5t2i(e38cg;INB!&yj8;|= zyoLfhX&R^2+OlmB(EmA_w*jq=(L( zr_R=V@EKf!|FEU2=5uP9PgO}%{~G$&PVhIl8i5(CNwcUP9lo3W3f|*n6w;?k%wtz= I-&x=L2g6u%4FCWD literal 0 HcmV?d00001 diff --git a/users/__pycache__/urls.cpython-38.pyc b/users/__pycache__/urls.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eb13081f826aef48f7573132922ddd472a6c4d03 GIT binary patch literal 561 zcmY*V&5qMB5O)5Ww53&6h%5JAYV8dngg~qWdw~jA4!%UDo;GRiII^7$+jrm{;K&>B zEWUEuS3n#XXE`im`5S+q$1~%(S}bM+?P>go|BMLv707)nki5WczaR)AB1u%lA{DbZ z3OtsHN?EEhmLX3>Dkmyuxhhx@5r{+v`M2qJA|@jLnjkMA9r^ShpF#E?pF=V72jPC7 zvYA+XAgkpqRwt|2ryX~-&zp8DdjTjs{HTx6zWeNW({6ZUJ~c4-aunag&Va3ha*JOJ z&zG;kU0EJvP^UCsSm9C*9e>)uFfVytTt1ojwvan;UXvO$|;WgFE=I1FxmjSX6phR6)TXLmv# zgfM=eb6W+?&oL(s0L!)Z2Qg)c2=Mz24PUTysJUyb)You+{)AqDJv!adjnN0#I$L!& s)H=RB;=S2PJyg7FXg|IT-LAVEUp!M<^b($jiPXxbAKF{ix>lLBU#OBtmgER ztmh5ZaC$78d5g82UY48r7Ta=qMQ-OiY=@E;gm3Wbd%~+C9PM8FY!8qcuLDvCzxLPsSuqB%dLo8O=(B!tF3{d`%F8z<&Bn)R zrhdu94B(B|5YVwIhMB~rwx22iJ0SQ9;z?z8CDZ^9u--f{o%89?n43sTYn)8E_rx-gxd~q{-=hdqWu!OjzJh8_L{;xuV?9jLI{=!w2(tv zQsV7YXk&v^okFh8XZZg>)DDdL=ctRD=x-UPDXSdaC$@E zh4XOaagX~qR6Ux9zx#g>N=yJKG2tlU!HoyZhCL{?fTt$ygVIoGrbQxt8;FT<#jD`< zlXgixfS+vwGs{9;q|U~fPA;G{IaEaoB$i>An_`?I)h?tRQ9HW^|RQa zx%B9>LYcwTXywBkTZdP`Btr#tIxEziTvoNJ0mNDhXvL~_ z>9&m%0jUxfVp-GfzS!D=$xj&OcZ~^04ANoa9~Kw;M+%lk2uq>qHC;(mJ@{zV-z@(N DPe;Fg literal 0 HcmV?d00001 diff --git a/users/migrations/0005_auto_20210814_0546.py b/users/migrations/0005_auto_20210814_0546.py new file mode 100644 index 0000000..022bda2 --- /dev/null +++ b/users/migrations/0005_auto_20210814_0546.py @@ -0,0 +1,36 @@ +# Generated by Django 3.2.6 on 2021-08-14 02:46 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0004_alter_user_id'), + ] + + operations = [ + migrations.CreateModel( + name='UserProfileContactInfo', + fields=[ + ('user_profile', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='users.userprofile')), + ('website_link', models.URLField(blank=True, null=True)), + ('github_link', models.URLField(blank=True, null=True)), + ('twitter_link', models.URLField(blank=True, null=True)), + ('website_link_active', models.BooleanField(default=False)), + ('github_link_active', models.BooleanField(default=False)), + ('twitter_link_active', models.BooleanField(default=False)), + ], + ), + migrations.AddField( + model_name='userprofile', + name='about', + field=models.TextField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name='userprofile', + name='title', + field=models.CharField(blank=True, max_length=100, null=True), + ), + ] diff --git a/users/migrations/0006_alter_user_id.py b/users/migrations/0006_alter_user_id.py new file mode 100644 index 0000000..13f634f --- /dev/null +++ b/users/migrations/0006_alter_user_id.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.6 on 2021-08-14 02:51 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0005_auto_20210814_0546'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='id', + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + ] diff --git a/users/migrations/__pycache__/0001_initial.cpython-38.pyc b/users/migrations/__pycache__/0001_initial.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f2bf720b5e164fcbc7e2d883cbb8b70761118fc7 GIT binary patch literal 2391 zcmZuzOOqQl5cZ76&-FgDyII0Z5CSnt#=C()LP07o5~w1n6xroq4pZ_-w&j&2N7C%Z zbE1IjUO90{xbc^C<+N9hR6%HIybp@Z)X3^qt6TcMZpl0I^UV@Gzdc*QKhBj(e>mav z6M=9GK7A7!QX&B_bp!t!`rnY3$5A&LSGtv;7_WAN2PIM_(a$9k(P&irsobrR;7O@n zIRqc2NA1uy$84amVNz&Y9ZN#FPTkn)X9I*=@abMqDp4P`8G);)QGCDI_B_EKBe(i10Ho(_9`OwRkKwbm+GPy!x(jk|~ zreD|AOn=quKO!F&`oRKNU4zJ<{8}cT&h}J$zW$=r2NcQmg9xxZOKt!=pV`L4BUZM7 zqX=ha*uAj1xpg(c+)$Nd8k#;++9U#xX)?yo5>Cay3~jUnV>V>uz74;=*N$upX3Hd1 z6dOwH+(fZ4R?}pcPHh8B8RjgfwzWr9PipG@+4=-)y)P9x`~i6PX>uGvwLoQ?97Av} z2TbI?k9@yfwv`^oV%J6@)Iwthgjhh z2+>F?)TjGY#poMqoaQnjY`_d`NDy?Z6L&6ku5=P41)s);zc^H@c7ZA-RWhbp;{nxb z4b*DUHD^7>d%z9BMk+l6{e<$o>HEm-q+M3a$1^=U4bP4n%YE0I*$N$F#t;7i+kD>l z5hg@IbX#{68QkyZH%`&BKcs-LaxfPVrC2zg5ferhykRnC0%>MwoVUD(83(ojE&&|2 zM|1j0pGT}~;FlBBqsSCV3IwBKW{h@(rktj;EMY<$AOy)if|H6GmjgIWWhQ_fTP-Hp zIlzo2Bgq6Mxff6&v_rM(fbtksii8bMu(Q>HlG#M(cpElc^ow2k?Ceo4#Q>64#^^X@ zAX^92{4ZgP zi@IDnk|1xPhJ6}`kRN_SOU_Yps&?}8PbM^FeU|>GqWX}l$2Aw{i$3z>Z`#5R)zE})FlB(bd|d75GF=7UsYf*CldY>Utd6@-eELAitupt-v~X&$<(OO-uj zci7-IoO$xz;z(u)#*?XXeFdzc!>Am-j4DwOhO5C+umbNz-@4vPunxM7VB^nl zISk_&S@)sCrIkJk<9UBMVfPDAxQT!D z6)xZbQC{YDsz^>Vn){hgob0P053`VXKX?#8_=^Xjp!4WwKZ}6e_tI#E@!s8(+9a1V z&5bOJ%0_t!LRBfTer+Zb=d$}9qi_@dDj=RGyo`XCQ6Mvt`68HyKqVGlQp6F^85VAE z2PlLPUE#O&SYVei53_SjGG7YZycRfjOkZBx41&9Be#5p zi@f(I^{~Z}c_E8wiKY6yHYF>{r;G#G4u~nQmAR7!dVv0I^>8YM0=vJ^0#=G;bgA)nO3c4fu* z#K;Sg5`EQ0$Ec|cpH6M;_;j?aUZLS`dT3+Tu)>&47{d{+l|bBQ>`Bd)^K=r3g}{2n zM2bpNm-672GD4p@n(wtuv4>mL*=1jT5_x$c?lkw0Zviw&`!2=cHeUUK%r{oYgh5fe zcWRR+TN{{s?{44X{Iw?*~Xl#Ahyx}1xNso3KA5Gk5GM!TLW*Up6;HX}LQKJp8l_N2qpAo5Ulq%<&FOJom?mM-ymvYNb=u03)#CwTiq6+ z_4nokX-AK+zU{)C7qjvN9yR@H7|)K^%GyX(oj9&vlts&T6TD@uMPIl~@z?ECeB+u{ MMecHBpY;9z0Xq$T#Q*>R literal 0 HcmV?d00001 diff --git a/users/migrations/__pycache__/0003_auto_20210812_1631.cpython-38.pyc b/users/migrations/__pycache__/0003_auto_20210812_1631.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5c37c9f734e97777034c24cf6a031dd407d94bb3 GIT binary patch literal 1188 zcmY*YOK;mo5GI$TMCxHXcG{#s+eTtrX zwaM)bL9fRh-h+PXf|m|>pSvjD#$FLV!03<<`G}7<1pOWKN0{G5+1pqcAs88R7k1L! zw4d&!w_ra#;J0)xw)y^s#3Z`S53rZp$~(D!ICkMQ704>OGo4Onlr?ohXVY0YeGrB; zoP|FqTj#OP)!&?Dpd83pA~0mP?rxJOFB_nB5ktKOsfG)z{6!Y4M1Ud*e>V4R)#tEc zji{9iJgM0^riP_VGA1wS1zai*_asatSDVKwOO7&O(rm(;vg-c3&kPLpti5cPdE zc%HEs+Gdsabj@UEj$h<3FHm)EubY%2{9L(h7f4{`o*m8U>EifU5`D$)lg0dJx;Q)f zm7csfJUUi`qJ%Bi|1#tRYg+a1D$rn8!K*b+uoA^@5QYzeL#Qt5q70A(DJ<)XmX|?Q zv*iWmNda8N?-r6>&^-= Vrrd`eypJ@1m5XYGPjOAx`VU$=U8w*7 literal 0 HcmV?d00001 diff --git a/users/migrations/__pycache__/0004_alter_user_id.cpython-38.pyc b/users/migrations/__pycache__/0004_alter_user_id.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c8bc2285de83aa4b6a82c3b9e60b4ad71ee32516 GIT binary patch literal 668 zcmYjP!EVz)5MA5r#&)SxaiD@f@FkAZ2=#!fB2`5ksE{BRUsl`9B&@TE&8|%qu5hFu z!cXuad*#G0AfYnrg4(g>X=moGXWrQJgM*BbeH$Oi9|2=OeelpY1!v@TOaKf5%{D<5 zY{DuGd>rbiie52@fPZDcF{t^MsNxX5V|o0G?yx*`*+#95>{PS0Zg0~7YJ0ixwTG5c za7J!t1jwoYSQSFB3aSWs9YY8a?$skq>J&J{e4s=8&TJ|w`i<8 znoK6gLiSxFrjuzoIVq>2e12Ry?&~g8aPdCpZUnfLz3yCspgMV_(e1ZJZKTM?G=z7!kNpO7XK~tJ2Ov-I>2vP%)Q>?`U7p+>gE%c zYyY?+3m+J)@jUUfk{J)C;$s Ib{HhlUl@S4qW}N^ literal 0 HcmV?d00001 diff --git a/users/migrations/__pycache__/0005_auto_20210814_0546.cpython-38.pyc b/users/migrations/__pycache__/0005_auto_20210814_0546.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9601d6f719c21d260078c428946413ab60be19ef GIT binary patch literal 1206 zcmZ8gOK;Oa5VqHL;yn9;mP(wFa>=7=Q4a{Uq!c01f@pimWwq%}5<43^tT#>5D_rvf z_zC{+E!v~Yc{Z?Hqpvc4IxxLgE#c#=92ZgE>! zBfDddNQc-46XA5+R~C1;`^n-i*rE5)>3E!+ST*k&eXJVM#Zl0gOa*b2=-epgAd=&A zqgz~M1bcXruSme^*xc$6Zujku18#^BxdXW{4}Mq(b73(oahH2M$MbWyN4Sr=GD=)t zm=nas|8VI)Tt-|$|H|A(>*_yfX%Y4Fe0ff=f+}C(tEgGR9BX(U%pLx4ZfY}{b<`}Q zbmLyrcRDNl5%ytI7mn^4fZgyW3CKjRHk-|D%7g+*jf4hVub(0FPR6}JKs$~UJ6Erx zUaZTh!8lW#9$u}aA?k+S3Tv(|#1X~Tf&yI{%V5Ohj1FO@3uqNE5nO>L3S&*7n*<7| z2%@1Wrz==A)NV8pLYMl18ce#WPM6g*P^LU%sn-_$C{`9+K+V>94m~yzO0O=s-m3N5 zVuU*%g1OiTbgmbG;E66R{D$^eH=d~L8=QffBdhqq>0y^tK-5!AfljOu>=K zAsr*Byp7;AMsjFM);S)ss2^*>yV_4%j-##f?fsMX{=rM_pS^#R8J1tfu>clj$lvEY z;|iy6sczZ!fXR$4#$&jBCSNSs#KbB2*=wBP6()`80N+p|;tOcCb{hwf3{^aC;JAcv zt`a()H4>bdA?jsM#8b+~K_eZM#{K(i;MDCI%YHqcfyRB{7+}VCwAJ3$D>v=qh zN$or2#`RpAkjM6Nx~n$sALO@NMk(&1UuVmxx%#{ z!igjQ;wvY90ST3H3Tnsl$TRQBkx+3=+b$>} zD*|9e2*ENaIEpIr18`LBfS?t@2zKrS#CM$V7!n$my5#NuDKs+LSTwen=6QY~)uwOd zB45n&qxrtf5B3k8@VE;#d^mUEo3oj4vz05{x`en|)%r8K@eP`* z)}mai24|e4c{xMngzvfNT4N0tv&6-6=u7ERDT%kKkg`kf<+ZVi6Hthh(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o10eKeRZts93)| zBe5j2I48d>)zHvF-zBv;yClCrKcFZ-D>b>KIKH4#zqlkZIU6Wnl$MiU9-ml{sb31# qnVXqj1k{_KSF9f&pP83g5+AQuP str: + """ + Hash value passed by user. + + :param value: password of a user + :return: a hashed version of the password + """ + return make_password(value) + # def validate(self, data): # print(data) # return data @@ -41,9 +51,9 @@ class Meta: model = UserProfile exclude = ('id', ) - def create(self, validated_data): - print(validated_data) - return super().create(validated_data) + # def create(self, validated_data): + # print(validated_data) + # return super().create(validated_data) diff --git a/users/views.py b/users/views.py index 85864c2..7962f66 100644 --- a/users/views.py +++ b/users/views.py @@ -35,10 +35,10 @@ def post(self, request, *args, **kwargs): return Response(serializer.data, status=status.HTTP_201_CREATED) -class CreateUserProfileAPIView(generics.CreateAPIView): - permission_classes = (IsAuthenticated, ) - model = UserProfile - serializer_class = ... +# class CreateUserProfileAPIView(generics.CreateAPIView): +# permission_classes = (IsAuthenticated, ) +# model = UserProfile +# serializer_class = ... From f35f34410d5f907b2fbff0a8b9d10bb44cad90c8 Mon Sep 17 00:00:00 2001 From: whatislove118 Date: Sat, 14 Aug 2021 19:01:30 +0300 Subject: [PATCH 6/8] create_user_profile --- questions/migrations/0001_initial.py | 40 ---------- questions/migrations/__init__.py | 0 .../__pycache__/0001_initial.cpython-38.pyc | Bin 1349 -> 0 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 182 -> 0 bytes .../__pycache__/settings.cpython-38.pyc | Bin 3599 -> 3757 bytes stackoverflow_api/settings.py | 14 ++-- users/__pycache__/models.cpython-38.pyc | Bin 4869 -> 5239 bytes users/__pycache__/serializers.cpython-38.pyc | Bin 1537 -> 1918 bytes users/__pycache__/urls.cpython-38.pyc | Bin 561 -> 642 bytes users/__pycache__/views.cpython-38.pyc | Bin 1727 -> 1814 bytes users/migrations/0001_initial.py | 50 +++++++----- users/migrations/0002_auto_20210811_1219.py | 71 ------------------ users/migrations/0003_auto_20210812_1631.py | 29 ------- users/migrations/0004_alter_user_id.py | 19 ----- users/migrations/0005_auto_20210814_0546.py | 36 --------- users/migrations/0006_alter_user_id.py | 18 ----- .../__pycache__/0001_initial.cpython-38.pyc | Bin 2391 -> 2474 bytes .../0002_auto_20210811_1219.cpython-38.pyc | Bin 1391 -> 0 bytes .../0003_auto_20210812_1631.cpython-38.pyc | Bin 1188 -> 0 bytes .../0004_alter_user_id.cpython-38.pyc | Bin 668 -> 0 bytes .../0005_auto_20210814_0546.cpython-38.pyc | Bin 1206 -> 0 bytes .../0006_alter_user_id.cpython-38.pyc | Bin 654 -> 0 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 178 -> 178 bytes users/models.py | 24 ++++-- users/serializers.py | 10 ++- users/urls.py | 5 +- users/views.py | 21 ++++-- 27 files changed, 80 insertions(+), 257 deletions(-) delete mode 100644 questions/migrations/0001_initial.py delete mode 100644 questions/migrations/__init__.py delete mode 100644 questions/migrations/__pycache__/0001_initial.cpython-38.pyc delete mode 100644 questions/migrations/__pycache__/__init__.cpython-38.pyc delete mode 100644 users/migrations/0002_auto_20210811_1219.py delete mode 100644 users/migrations/0003_auto_20210812_1631.py delete mode 100644 users/migrations/0004_alter_user_id.py delete mode 100644 users/migrations/0005_auto_20210814_0546.py delete mode 100644 users/migrations/0006_alter_user_id.py delete mode 100644 users/migrations/__pycache__/0002_auto_20210811_1219.cpython-38.pyc delete mode 100644 users/migrations/__pycache__/0003_auto_20210812_1631.cpython-38.pyc delete mode 100644 users/migrations/__pycache__/0004_alter_user_id.cpython-38.pyc delete mode 100644 users/migrations/__pycache__/0005_auto_20210814_0546.cpython-38.pyc delete mode 100644 users/migrations/__pycache__/0006_alter_user_id.cpython-38.pyc diff --git a/questions/migrations/0001_initial.py b/questions/migrations/0001_initial.py deleted file mode 100644 index 27bba48..0000000 --- a/questions/migrations/0001_initial.py +++ /dev/null @@ -1,40 +0,0 @@ -# Generated by Django 3.2.6 on 2021-08-10 13:01 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='Question', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('title', models.CharField(max_length=50)), - ('description', models.TextField()), - ('date_created', models.DateTimeField(auto_now_add=True)), - ('views', models.IntegerField(default=1)), - ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - ), - migrations.CreateModel( - name='Comment', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('description', models.TextField()), - ('like', models.IntegerField(default=0)), - ('is_useful', models.BooleanField(default=False)), - ('date_created', models.DateTimeField(auto_now_add=True)), - ('question', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='questions.question')), - ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to=settings.AUTH_USER_MODEL)), - ], - ), - ] diff --git a/questions/migrations/__init__.py b/questions/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/questions/migrations/__pycache__/0001_initial.cpython-38.pyc b/questions/migrations/__pycache__/0001_initial.cpython-38.pyc deleted file mode 100644 index af6ceae16e9824b94b654f24fc68120d0a0785af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1349 zcmZux&2rl|5GF`Uq9{ss9OuWKP7b~4BHFn$)9G~LN_OIDZMU_2;RQnpD;gB|6N0j> zT-&SPA-T1W&{x5=C*ON%4s92t6g$&`1I+GXvESm`1<$)(*TC`X^;YnZQmL9r#z4rEsrI`+Hc@$rQ+`1V1B`$$BQO~WEU@1-pc%K` zlZXTk*m0Yg%mO#=FoGXu!J0Sr%w#RJdCXz$8-kh(?z`@L1Emhyd+6y^_AY80tj9Jg zZ4SVcDpuc?4EDlb%rft(gR zuTVUOG1sBdv4%>sD;hGU8kZcd>ZuONyap3!$7ht9!rQT1#@UmQMOR%AwW0 zC{-?{*n0~BD0`H?S%g9(q1NIlFOiMH($Tl&RhZ}D1RWXVkTOW8%fi%K8~f+u*Yy1C z;3s-~Iy(4Xc^^GENYxouR3GaOFZ7F2-nTs3$08i+%c%D7A}m(o7_Q4&?2eGiF;AdY zy`!{*2v&aXVO9W-((kZlO%{h~lqr))jgepAGj%2Z)tr0>X3^^)!WXhD)uw zqf>fvI(~h0@=Cc`4y)(*n^Am;c~;8@>YrnMqGRyrRiY$g{ zDQUj+Wf@NAxL?eKu6WMOAq zadGy@vk&IqcBi)0mLB<>Jo^i+e^%%F4mM)N!(f`Fv$`(rm4!{J64u=Bd^8T~f6idd iS>fwD>um0BUrR60rktW9 diff --git a/questions/migrations/__pycache__/__init__.cpython-38.pyc b/questions/migrations/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 06255acb8c312fd30bfa644a3de73964058a6fbf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 182 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 ZtWC6l5y=me=>Kk~m?#fuPI07Y{R7(xb6fxb diff --git a/users/__pycache__/serializers.cpython-38.pyc b/users/__pycache__/serializers.cpython-38.pyc index 14ed136ede8beb8cafb6ea0bbbedeb9345416810..41d45cb22ddf90c9de73851f43959e73e1ca1c05 100644 GIT binary patch delta 571 zcmZutyGjF55WTaHWMi`HBQe2ej1X}ZECO2CTG$04*u-L4cW)qJF=2LN5duLHNGHht zfC?7DO8fy!zhNso|3aLb2c~df&Ye4R?#!KYm-;dEOI=@JWTo$%{rRSA0HrVshlU#; zuxiZJD$GnoneYKJ?^kdx^&CmCrzT>V{EXbs5(KOZeE=3fLu$x1y4^#Cji78Qk=nxg zO@NC;i{j1e26qBKN;=5xfLm>!<4D4sC>po6S9dAvT7)*PP;vrkwnp{X2NG>H%qtFpA=1nZX!e!Xb4p|Lc*;NKwRT(mc zRaM$v4fmDT-U)HW!!%F;Gj#h`2+_+I)juKn>{05%6Ul(|d1lJEPDiBr?G{JOQ;b^# zBZv}~<#@L5Th?q_stLj=>&>sac}ZejV6#^Dc!gt~)ar0Nn(S`IFeQ5G0t^A-JL5Ky pJ{{n2&zlVyi(3?ww15J^G5K^?Lm6kv;hy>o1(GuteyKZf@C(peZ%6n1vLT!3J=fAxH%ikl{CMV~Y=9Lud7eIvdqu8M=KTV#= nL5x96MVyltGRm+C0$p7sIQcMRjEDe`DZnVeB*F|NA@DZRH%FD~e00dS{qKP{g85kaeILLql$Z-JT;);pdhCYl8sf;PCsq70l7c!)< zrLgxh1Nj`OEMPt-l+TsQio)kk&w6Zg0>GEHV;RGw_lXvgfQ n$vt@vW6LaWCJQme2=M`#0*nGo0?a@X1pfd4;6)-@ diff --git a/users/__pycache__/views.cpython-38.pyc b/users/__pycache__/views.cpython-38.pyc index 2dd523d7d2cfb30676e36de5a7100ffe5bd6caa2..d7a19b6fa7f5e1ca9cff76c97309a4fc94415727 100644 GIT binary patch literal 1814 zcmZ`(OK%%D5GMDf)%)RBnii-JX#-uPIHw$n!a@?H?m;yir3H9dY(z>~c~`q3S5|E( zC#OgMfu7o9{t~Z65B&>0b%v`zvXic`pU7{xA;q+m8d2 zV|dl?0Gx0dlY}x#acsp_Vlz8&m}BO4>?R)bk`gPKxfArs~zbCvb>{pntfvNF2F!eRF#~Uw6xA_nF zN4iuurXmj(O2{Bdxrn=#_NQVlWHeUVQ+b##6mo+&PM3rErEZ?6!6JVn=6N&@bHP7Q z?VkyirE?`#v zKNd`m;Z=tKf-uSnvxGgR%n>f2$0@ho6IS9jgz4zU3z6f_gVU3rBe5Lg_a7e=3+F$< zt9*bQ1iPm2oi)_-NS1(tEgU7{i7rX;b|F-*ols68H|IKFz&S(3Q=`cXF?tELAz4 zu4({;Rv`T~`1$l||C7x1aPhEPk^8Vz9w6L9a1p*hz}nKTX?a;i;t=5*1gz&RFv@=k z9PVYh#3)Z4TO#|3_7uotTzUv_S9@$w)LxWXg_}mx_cq?IuZXq$!*2Di|5yZt|5(8C zJ_6py@>>KjPJMz4F=8by*C5}H1kNa(d? zD$k{jr(kKY`?oEYV4)`D!+YO(Z}Q%o`Q&~tyF2A_NrUHNt?mD| zChk1>I=MMBM>M7}BQj&lvtsJe+&7~_Y{rg>sBwYgf7Y40Z;oQ$wfa-7>_`qf)5!N zT|Ki0e8@$xCo?*^+*o(E_`cxs;6-xChY;Avjpw_m9}GABK)ek23CL4Bu-m3c_JfFn z+k7avpK=t{fV?NRx+E*4rCRznX{tbfObY6o-XSIROP{~fhN?vCDyGq`0IKjw3a|+! zRL?c_n#`&-2ptx#t=GtP-v9b2D`FB;KO6I3m=LaKfm zH*5pU^ A^8f$< diff --git a/users/migrations/0001_initial.py b/users/migrations/0001_initial.py index 7271e23..79f2a11 100644 --- a/users/migrations/0001_initial.py +++ b/users/migrations/0001_initial.py @@ -1,9 +1,8 @@ -# Generated by Django 3.2.6 on 2021-08-10 13:01 +# Generated by Django 3.2.6 on 2021-08-14 15:58 -import django.contrib.auth.models -import django.contrib.auth.validators from django.db import migrations, models -import django.utils.timezone +import django.db.models.deletion +import uuid class Migration(migrations.Migration): @@ -18,28 +17,43 @@ class Migration(migrations.Migration): migrations.CreateModel( name='User', fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('password', models.CharField(max_length=128, verbose_name='password')), ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), - ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), - ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), - ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), - ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), - ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), - ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), - ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), - ('login', models.CharField(blank=True, max_length=150)), + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('username', models.CharField(db_index=True, max_length=255, unique=True)), + ('email', models.EmailField(db_index=True, max_length=254, unique=True)), + ('is_active', models.BooleanField(default=True)), + ('is_staff', models.BooleanField(default=False)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')), ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')), ], options={ - 'verbose_name': 'user', - 'verbose_name_plural': 'users', - 'abstract': False, + 'unique_together': {('username', 'email')}, }, - managers=[ - ('objects', django.contrib.auth.models.UserManager()), + ), + migrations.CreateModel( + name='UserProfile', + fields=[ + ('user', models.OneToOneField(default='', on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='users.user')), + ('logo', models.ImageField(default='/media/users/default/logo.png', upload_to='media/default//logo/')), + ('location', models.CharField(blank=True, max_length=200, null=True)), + ('about', models.TextField(blank=True, max_length=255, null=True)), + ('title', models.CharField(blank=True, max_length=100, null=True)), + ], + ), + migrations.CreateModel( + name='UserProfileContactInfo', + fields=[ + ('user_profile', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='users.userprofile')), + ('website_link', models.URLField(blank=True, null=True)), + ('github_link', models.URLField(blank=True, null=True)), + ('twitter_link', models.URLField(blank=True, null=True)), + ('website_link_active', models.BooleanField(default=False)), + ('github_link_active', models.BooleanField(default=False)), + ('twitter_link_active', models.BooleanField(default=False)), ], ), ] diff --git a/users/migrations/0002_auto_20210811_1219.py b/users/migrations/0002_auto_20210811_1219.py deleted file mode 100644 index e090aed..0000000 --- a/users/migrations/0002_auto_20210811_1219.py +++ /dev/null @@ -1,71 +0,0 @@ -# Generated by Django 3.2.6 on 2021-08-11 09:19 - -import datetime -from django.db import migrations, models -from django.utils.timezone import utc - - -class Migration(migrations.Migration): - - dependencies = [ - ('users', '0001_initial'), - ] - - operations = [ - migrations.AlterModelOptions( - name='user', - options={}, - ), - migrations.AlterModelManagers( - name='user', - managers=[ - ], - ), - migrations.RemoveField( - model_name='user', - name='date_joined', - ), - migrations.RemoveField( - model_name='user', - name='first_name', - ), - migrations.RemoveField( - model_name='user', - name='last_name', - ), - migrations.RemoveField( - model_name='user', - name='login', - ), - migrations.AddField( - model_name='user', - name='created_at', - field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2021, 8, 11, 9, 19, 42, 187727, tzinfo=utc)), - preserve_default=False, - ), - migrations.AddField( - model_name='user', - name='updated_at', - field=models.DateTimeField(auto_now=True), - ), - migrations.AlterField( - model_name='user', - name='email', - field=models.EmailField(db_index=True, max_length=254, unique=True), - ), - migrations.AlterField( - model_name='user', - name='is_active', - field=models.BooleanField(default=True), - ), - migrations.AlterField( - model_name='user', - name='is_staff', - field=models.BooleanField(default=False), - ), - migrations.AlterField( - model_name='user', - name='username', - field=models.CharField(db_index=True, max_length=255, unique=True), - ), - ] diff --git a/users/migrations/0003_auto_20210812_1631.py b/users/migrations/0003_auto_20210812_1631.py deleted file mode 100644 index 34d6e95..0000000 --- a/users/migrations/0003_auto_20210812_1631.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 3.2.6 on 2021-08-12 13:31 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion -import uuid - - -class Migration(migrations.Migration): - - dependencies = [ - ('users', '0002_auto_20210811_1219'), - ] - - operations = [ - migrations.AlterUniqueTogether( - name='user', - unique_together={('username', 'email')}, - ), - migrations.CreateModel( - name='UserProfile', - fields=[ - ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), - ('logo', models.ImageField(default='/media/users/default/logo.png', upload_to='media/default//logo/')), - ('location', models.CharField(blank=True, max_length=200, null=True)), - ('user', models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), - ], - ), - ] diff --git a/users/migrations/0004_alter_user_id.py b/users/migrations/0004_alter_user_id.py deleted file mode 100644 index 10f472e..0000000 --- a/users/migrations/0004_alter_user_id.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.6 on 2021-08-12 13:36 - -from django.db import migrations, models -import uuid - - -class Migration(migrations.Migration): - - dependencies = [ - ('users', '0003_auto_20210812_1631'), - ] - - operations = [ - migrations.AlterField( - model_name='user', - name='id', - field=models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False), - ), - ] diff --git a/users/migrations/0005_auto_20210814_0546.py b/users/migrations/0005_auto_20210814_0546.py deleted file mode 100644 index 022bda2..0000000 --- a/users/migrations/0005_auto_20210814_0546.py +++ /dev/null @@ -1,36 +0,0 @@ -# Generated by Django 3.2.6 on 2021-08-14 02:46 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('users', '0004_alter_user_id'), - ] - - operations = [ - migrations.CreateModel( - name='UserProfileContactInfo', - fields=[ - ('user_profile', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='users.userprofile')), - ('website_link', models.URLField(blank=True, null=True)), - ('github_link', models.URLField(blank=True, null=True)), - ('twitter_link', models.URLField(blank=True, null=True)), - ('website_link_active', models.BooleanField(default=False)), - ('github_link_active', models.BooleanField(default=False)), - ('twitter_link_active', models.BooleanField(default=False)), - ], - ), - migrations.AddField( - model_name='userprofile', - name='about', - field=models.TextField(blank=True, max_length=255, null=True), - ), - migrations.AddField( - model_name='userprofile', - name='title', - field=models.CharField(blank=True, max_length=100, null=True), - ), - ] diff --git a/users/migrations/0006_alter_user_id.py b/users/migrations/0006_alter_user_id.py deleted file mode 100644 index 13f634f..0000000 --- a/users/migrations/0006_alter_user_id.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.6 on 2021-08-14 02:51 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('users', '0005_auto_20210814_0546'), - ] - - operations = [ - migrations.AlterField( - model_name='user', - name='id', - field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), - ), - ] diff --git a/users/migrations/__pycache__/0001_initial.cpython-38.pyc b/users/migrations/__pycache__/0001_initial.cpython-38.pyc index f2bf720b5e164fcbc7e2d883cbb8b70761118fc7..d5630f9e7df437d2b85263ff270e1460f0769d00 100644 GIT binary patch literal 2474 zcmZWr&2!sC6qjWA+o|0oO%rHA&<}8-c2Y_yP)gIJEuA5yLlX|>pjms@@;Ym+x>_}f zPt3s8e+d5xf5YAwF6736VFs9KdAsW*Zm>qrdcXJf?fYD5XKt?Iz_0%Md31TsaemXx z?9T`09^C0IAlSh!b4G5*9eEwk)x5`Y9pC)tJNZWr&SC#22m8bw7Jkfi3fOz*H1mhB z)@gdGGNO?{lJYoJg%QVurB8ZVHv3xw<{sSXU#{a2r{iL$<6*ax!(QZee3Bm)^e&`0 zEa5!%abe>1JzNC7j9s`&xID=LQURobtGI^e@EL>D0jZMOa1Kxx?#9Fiy|c!Wi{}Ab zFnWuCoB{6KF|uTAx%m9Vg%!(&t4_HU;PlRK7}=Xs?ty6}1OZuCt6cp<^DX^43wSY(wMtD3gQE zmDSa?8zEwnh%ifm^r=W?7^4vhM`%A}B#z`j`8yy}r39tv9vArV2WTeKd?zZk7esV< zO3YeSVF=7H9?^Jgo&)pT%&B~rp?FvMamJX?gQ99Q4bvVZVX&?tk z0-?s1fb1uX_NZhB0eA@pXdJ;(GHQ(*$JT>XBAKP& zMiG-f%9vE;0bxlf$-cDScoa8%QP+-@2&NKs8Bx_l&=C>`;VwB)WpITeM#n^zv|Bn^ zhp%)-a;k*8A&oKF7iDdskj3;zlH#v@2aR|pO zfst9z_7*j{3PkzP<9vLvJ%YYLZL_jHZL_u(wURg*U$fF9{7yHcOkSsP(9dG%3LXa( z-!l%ToNf=Un!e}+MFuQ0v32J^u{|H-{T{Z=uG*?t9wC7W(Dq#q(yo`}BRJAaCo*|~I z3S5!KRKk#|8YT&WfsA{Uq{7olsBV}c+tM#fk<&7$j)-Mz_c><- z#g;4Y>^$DIkA9X>tn!-PRFwyMyseltW*aDBlSDI)iPZDCEw7;r!$9BnG OUF@^~q~Vs_Qtm%6A^e8` literal 2391 zcmZuzOOqQl5cZ76&-FgDyII0Z5CSnt#=C()LP07o5~w1n6xroq4pZ_-w&j&2N7C%Z zbE1IjUO90{xbc^C<+N9hR6%HIybp@Z)X3^qt6TcMZpl0I^UV@Gzdc*QKhBj(e>mav z6M=9GK7A7!QX&B_bp!t!`rnY3$5A&LSGtv;7_WAN2PIM_(a$9k(P&irsobrR;7O@n zIRqc2NA1uy$84amVNz&Y9ZN#FPTkn)X9I*=@abMqDp4P`8G);)QGCDI_B_EKBe(i10Ho(_9`OwRkKwbm+GPy!x(jk|~ zreD|AOn=quKO!F&`oRKNU4zJ<{8}cT&h}J$zW$=r2NcQmg9xxZOKt!=pV`L4BUZM7 zqX=ha*uAj1xpg(c+)$Nd8k#;++9U#xX)?yo5>Cay3~jUnV>V>uz74;=*N$upX3Hd1 z6dOwH+(fZ4R?}pcPHh8B8RjgfwzWr9PipG@+4=-)y)P9x`~i6PX>uGvwLoQ?97Av} z2TbI?k9@yfwv`^oV%J6@)Iwthgjhh z2+>F?)TjGY#poMqoaQnjY`_d`NDy?Z6L&6ku5=P41)s);zc^H@c7ZA-RWhbp;{nxb z4b*DUHD^7>d%z9BMk+l6{e<$o>HEm-q+M3a$1^=U4bP4n%YE0I*$N$F#t;7i+kD>l z5hg@IbX#{68QkyZH%`&BKcs-LaxfPVrC2zg5ferhykRnC0%>MwoVUD(83(ojE&&|2 zM|1j0pGT}~;FlBBqsSCV3IwBKW{h@(rktj;EMY<$AOy)if|H6GmjgIWWhQ_fTP-Hp zIlzo2Bgq6Mxff6&v_rM(fbtksii8bMu(Q>HlG#M(cpElc^ow2k?Ceo4#Q>64#^^X@ zAX^92{4ZgP zi@IDnk|1xPhJ6}`kRN_SOU_Yps&?}8PbM^FeU|>GqWX}l$2Aw{i$3z>Z`#5R)zE})FlB(bd|d75GF=7UsYf*CldY>Utd6@-eELAitupt-v~X&$<(OO-uj zci7-IoO$xz;z(u)#*?XXeFdzc!>Am-j4DwOhO5C+umbNz-@4vPunxM7VB^nl zISk_&S@)sCrIkJk<9UBMVfPDAxQT!D z6)xZbQC{YDsz^>Vn){hgob0P053`VXKX?#8_=^Xjp!4WwKZ}6e_tI#E@!s8(+9a1V z&5bOJ%0_t!LRBfTer+Zb=d$}9qi_@dDj=RGyo`XCQ6Mvt`68HyKqVGlQp6F^85VAE z2PlLPUE#O&SYVei53_SjGG7YZycRfjOkZBx41&9Be#5p zi@f(I^{~Z}c_E8wiKY6yHYF>{r;G#G4u~nQmAR7!dVv0I^>8YM0=vJ^0#=G;bgA)nO3c4fu* z#K;Sg5`EQ0$Ec|cpH6M;_;j?aUZLS`dT3+Tu)>&47{d{+l|bBQ>`Bd)^K=r3g}{2n zM2bpNm-672GD4p@n(wtuv4>mL*=1jT5_x$c?lkw0Zviw&`!2=cHeUUK%r{oYgh5fe zcWRR+TN{{s?{44X{Iw?*~Xl#Ahyx}1xNso3KA5Gk5GM!TLW*Up6;HX}LQKJp8l_N2qpAo5Ulq%<&FOJom?mM-ymvYNb=u03)#CwTiq6+ z_4nokX-AK+zU{)C7qjvN9yR@H7|)K^%GyX(oj9&vlts&T6TD@uMPIl~@z?ECeB+u{ MMecHBpY;9z0Xq$T#Q*>R diff --git a/users/migrations/__pycache__/0003_auto_20210812_1631.cpython-38.pyc b/users/migrations/__pycache__/0003_auto_20210812_1631.cpython-38.pyc deleted file mode 100644 index 5c37c9f734e97777034c24cf6a031dd407d94bb3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1188 zcmY*YOK;mo5GI$TMCxHXcG{#s+eTtrX zwaM)bL9fRh-h+PXf|m|>pSvjD#$FLV!03<<`G}7<1pOWKN0{G5+1pqcAs88R7k1L! zw4d&!w_ra#;J0)xw)y^s#3Z`S53rZp$~(D!ICkMQ704>OGo4Onlr?ohXVY0YeGrB; zoP|FqTj#OP)!&?Dpd83pA~0mP?rxJOFB_nB5ktKOsfG)z{6!Y4M1Ud*e>V4R)#tEc zji{9iJgM0^riP_VGA1wS1zai*_asatSDVKwOO7&O(rm(;vg-c3&kPLpti5cPdE zc%HEs+Gdsabj@UEj$h<3FHm)EubY%2{9L(h7f4{`o*m8U>EifU5`D$)lg0dJx;Q)f zm7csfJUUi`qJ%Bi|1#tRYg+a1D$rn8!K*b+uoA^@5QYzeL#Qt5q70A(DJ<)XmX|?Q zv*iWmNda8N?-r6>&^-= Vrrd`eypJ@1m5XYGPjOAx`VU$=U8w*7 diff --git a/users/migrations/__pycache__/0004_alter_user_id.cpython-38.pyc b/users/migrations/__pycache__/0004_alter_user_id.cpython-38.pyc deleted file mode 100644 index c8bc2285de83aa4b6a82c3b9e60b4ad71ee32516..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 668 zcmYjP!EVz)5MA5r#&)SxaiD@f@FkAZ2=#!fB2`5ksE{BRUsl`9B&@TE&8|%qu5hFu z!cXuad*#G0AfYnrg4(g>X=moGXWrQJgM*BbeH$Oi9|2=OeelpY1!v@TOaKf5%{D<5 zY{DuGd>rbiie52@fPZDcF{t^MsNxX5V|o0G?yx*`*+#95>{PS0Zg0~7YJ0ixwTG5c za7J!t1jwoYSQSFB3aSWs9YY8a?$skq>J&J{e4s=8&TJ|w`i<8 znoK6gLiSxFrjuzoIVq>2e12Ry?&~g8aPdCpZUnfLz3yCspgMV_(e1ZJZKTM?G=z7!kNpO7XK~tJ2Ov-I>2vP%)Q>?`U7p+>gE%c zYyY?+3m+J)@jUUfk{J)C;$s Ib{HhlUl@S4qW}N^ diff --git a/users/migrations/__pycache__/0005_auto_20210814_0546.cpython-38.pyc b/users/migrations/__pycache__/0005_auto_20210814_0546.cpython-38.pyc deleted file mode 100644 index 9601d6f719c21d260078c428946413ab60be19ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1206 zcmZ8gOK;Oa5VqHL;yn9;mP(wFa>=7=Q4a{Uq!c01f@pimWwq%}5<43^tT#>5D_rvf z_zC{+E!v~Yc{Z?Hqpvc4IxxLgE#c#=92ZgE>! zBfDddNQc-46XA5+R~C1;`^n-i*rE5)>3E!+ST*k&eXJVM#Zl0gOa*b2=-epgAd=&A zqgz~M1bcXruSme^*xc$6Zujku18#^BxdXW{4}Mq(b73(oahH2M$MbWyN4Sr=GD=)t zm=nas|8VI)Tt-|$|H|A(>*_yfX%Y4Fe0ff=f+}C(tEgGR9BX(U%pLx4ZfY}{b<`}Q zbmLyrcRDNl5%ytI7mn^4fZgyW3CKjRHk-|D%7g+*jf4hVub(0FPR6}JKs$~UJ6Erx zUaZTh!8lW#9$u}aA?k+S3Tv(|#1X~Tf&yI{%V5Ohj1FO@3uqNE5nO>L3S&*7n*<7| z2%@1Wrz==A)NV8pLYMl18ce#WPM6g*P^LU%sn-_$C{`9+K+V>94m~yzO0O=s-m3N5 zVuU*%g1OiTbgmbG;E66R{D$^eH=d~L8=QffBdhqq>0y^tK-5!AfljOu>=K zAsr*Byp7;AMsjFM);S)ss2^*>yV_4%j-##f?fsMX{=rM_pS^#R8J1tfu>clj$lvEY z;|iy6sczZ!fXR$4#$&jBCSNSs#KbB2*=wBP6()`80N+p|;tOcCb{hwf3{^aC;JAcv zt`a()H4>bdA?jsM#8b+~K_eZM#{K(i;MDCI%YHqcfyRB{7+}VCwAJ3$D>v=qh zN$or2#`RpAkjM6Nx~n$sALO@NMk(&1UuVmxx%#{ z!igjQ;wvY90ST3H3Tnsl$TRQBkx+3=+b$>} zD*|9e2*ENaIEpIr18`LBfS?t@2zKrS#CM$V7!n$my5#NuDKs+LSTwen=6QY~)uwOd zB45n&qxrtf5B3k8@VE;#d^mUEo3oj4vz05{x`en|)%r8K@eP`* z)}mai24|e4c{xMngzvfNT4N0tv&6-6=u7ERDT%kKk str: """ return make_password(value) + def create(self, validated_data): + print(validated_data) + return User.objects.create_user(**validated_data) + # def validate(self, data): # print(data) # return data @@ -51,9 +55,9 @@ class Meta: model = UserProfile exclude = ('id', ) - # def create(self, validated_data): - # print(validated_data) - # return super().create(validated_data) + def create(self, validated_data): + print(validated_data) + return super().create(validated_data) diff --git a/users/urls.py b/users/urls.py index f3f31ee..54ac3b1 100644 --- a/users/urls.py +++ b/users/urls.py @@ -1,11 +1,12 @@ from django.urls import path, include from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView -from .views import GetUserAPIView, CreateUserAPIView +from .views import GetUserAPIView, CreateUserAPIView, CreateUserProfileAPIView urlpatterns = [ path('hello/', GetUserAPIView.as_view(), name='hello'), path('auth/', TokenObtainPairView.as_view(), name='auth'), path('refresh/', TokenRefreshView.as_view(), name='auth'), - path('register/', CreateUserAPIView.as_view(), name='reg') + path('register/', CreateUserAPIView.as_view(), name='reg'), + path('accounts/profile/', CreateUserProfileAPIView.as_view(), name='profile') ] \ No newline at end of file diff --git a/users/views.py b/users/views.py index 7962f66..7e8a76c 100644 --- a/users/views.py +++ b/users/views.py @@ -1,16 +1,13 @@ from django.contrib.auth import get_user_model -from django.shortcuts import render from rest_framework import generics, status # Create your views here. from rest_framework.permissions import AllowAny, IsAuthenticated from rest_framework.response import Response from rest_framework_simplejwt.authentication import JWTAuthentication -from rest_framework_simplejwt.tokens import RefreshToken -from rest_framework_simplejwt.views import TokenObtainPairView from users.models import UserProfile -from users.serializers import CreateUserSerializer +from users.serializers import CreateUserSerializer, UserProfileSerializer class GetUserAPIView(generics.RetrieveAPIView): @@ -27,7 +24,9 @@ class CreateUserAPIView(generics.CreateAPIView): model = get_user_model() serializer_class = CreateUserSerializer + ## этого можно было и не делать, но чисто для себя я написал def post(self, request, *args, **kwargs): + print(request.user) user = request.data serializer = self.serializer_class(data=user) if serializer.is_valid(raise_exception=True): @@ -35,10 +34,16 @@ def post(self, request, *args, **kwargs): return Response(serializer.data, status=status.HTTP_201_CREATED) -# class CreateUserProfileAPIView(generics.CreateAPIView): -# permission_classes = (IsAuthenticated, ) -# model = UserProfile -# serializer_class = ... +class CreateUserProfileAPIView(generics.CreateAPIView): + permission_classes = (IsAuthenticated, ) + authentication_classes = [JWTAuthentication, ] + model = UserProfile + serializer_class = UserProfileSerializer + + # def post(self, request, *args, **kwargs): + # print(request.user) + # return Response('2') + From 2a0779538ac87b76788e1958ddac188610db495a Mon Sep 17 00:00:00 2001 From: whatislove118 Date: Fri, 20 Aug 2021 22:02:29 +0300 Subject: [PATCH 7/8] finish_user_profile --- .../__pycache__/settings.cpython-38.pyc | Bin 3757 -> 3757 bytes stackoverflow_api/settings.py | 5 +- users/__pycache__/models.cpython-38.pyc | Bin 5239 -> 6250 bytes users/__pycache__/permissions.cpython-38.pyc | Bin 0 -> 639 bytes users/__pycache__/serializers.cpython-38.pyc | Bin 1918 -> 2250 bytes users/__pycache__/urls.cpython-38.pyc | Bin 642 -> 705 bytes users/__pycache__/views.cpython-38.pyc | Bin 1814 -> 3101 bytes users/migrations/0001_initial.py | 29 ++++---- ...ename_user_profile_userprofile_contacts.py | 18 +++++ .../__pycache__/0001_initial.cpython-38.pyc | Bin 2474 -> 2637 bytes ...rofile_userprofile_contacts.cpython-38.pyc | Bin 0 -> 595 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 178 -> 178 bytes users/models.py | 68 ++++++++++++------ users/permissions.py | 10 +++ users/serializers.py | 22 ++++-- users/urls.py | 7 +- users/utils.py | 11 +++ users/views.py | 62 ++++++++++++---- 18 files changed, 173 insertions(+), 59 deletions(-) create mode 100644 users/__pycache__/permissions.cpython-38.pyc create mode 100644 users/migrations/0002_rename_user_profile_userprofile_contacts.py create mode 100644 users/migrations/__pycache__/0002_rename_user_profile_userprofile_contacts.cpython-38.pyc create mode 100644 users/permissions.py create mode 100644 users/utils.py diff --git a/stackoverflow_api/__pycache__/settings.cpython-38.pyc b/stackoverflow_api/__pycache__/settings.cpython-38.pyc index c4c319ff088d5645533a985454ddea65548d7a7b..c9e5e5958acd6899870c1c17a0f45c5341a71962 100644 GIT binary patch delta 119 zcmZ20yH=Jrl$V!_0SLN&%O^hG$eYAyYTe72!Ul%6saz>cP-vH8pW=|>nBtV;oZ^z= zn&OtqoMM&Y-pdH2ZBnc^FJbg%=f1^~l2}>%k`ZX$WLC~oT#QldxtV#TC8@=m&v9;I LVPu)y$fpAUxDz5_ delta 119 zcmZ20yH=Jrl$V!_0SG>uh$lYV$eYAyYTL`0!Ul$Rsaz>cP-vgxkm8u)l;WJ?lH!`; zmg1huoMM$?-OC81ZBlGEFJbg%7mH%g&CDw;NiBZK2sBcY@fJ%;VrB8ll`!_09O7=Ggcs4SQl^Y-;ysZ3L~>#>~<~`v@Y}u)&eHL-K*ibR%Ch3xc$b7$_e9hSmuox!LPYX}Sf6veDAvG}MqwP`5`hFM*-=Dg5yzpnX|3!j)=P@`NzVIjK zvDbEeHkgTZ`&aJQf*k^fZ+1i^(P8{IyJl}q3@tvEt-8t*V@kx9luK+;S!BK%Y2ejG z6}-Nvu4Jww85Y?}Ccc#YQ*{7Vr3DJl8PS=XZUtgj{H!?=n2k<5syCugP^!9`+@@zy zM*4F-q*sSM#v@w*Ohc2oO_{be zvthQfqLTTtV%7P30MwEXk~{0aPVOWRl8@#0Hu$wG#fOdIi3vEd27{$TrDytH6?;g#mx#=AneQ^y z$@TA`g;es2>ac|zMQSXpzZHLGANI+sC}go8U*QsY1|%)cx2JzGANWxR8S`c!wnSxf z?o=NSqWZglhboB4#8G_Q(XDgElg^zn6XHO*XT)!Q?v-o}x5N_-2ePq_B>4IY46nDK+ki|m#7f0dVCdze&`lcjSQ zpg*9=lLxm1iU+T1F^dW6`^ugTD38*B-^I(t-QBCYmkqS09++Im*T6Yl7F4hx%s(Pr_NFNVhr-%Ph6*#y9zS;W_^}fw<-IRmCytOl|ASflB=wF2 zE52LW$4yN7^bkqhOw;mE6jiO18qR F{{uH-i{1bL delta 1763 zcmZ`(O>Epm6!!RM?e*GT?@xBqbkl4~)5J-L0<>yr+A3*CN~I*FYI0c+#?4IHgxz%* zdxfaBL?XSR2%-T*Z;4bPl?#UmaX{iqIe^jw7bDIP1QIv6!5c56n<}+6pWmO~oB7_% z{5Ja6fVE(nx&Y5lzZAXAo=vNSKHnU=_Ik~%m`K0^R;yMmT}dP1vVb+5yer@&k=Lb) z4Z4m~pr?pJv~{_X0gHi6u$V+&$wEvHEEZ0KCHz_u2Q_$6e699L_`c^6O&d z2=0K+6(X6JW-K{l4)Ija0z5!;fc>52R$Up>Li5EC!?;36Z{b%T() z?()u$`aLx898dW?hbQ99n_iCiZJ~7^Po*HcN;NX@|!#KLZE|_I@${u^e zgv@h5{&8O684f1^qI9Fa{8odwp$|XaBDAgeK4+0UOG57*2oLx!(GWcYpE;+iJsJDK z9?kOAM*39ETdqzolPacZW@oxlHyg>!jC1BA)xWVq7Q=wP02<|cjh^PfGago(o&?7j zjG)B6%k-3EeG&8nj|$p2jG)d2JC3elc|)nFL|W0Xf)$cn(YI2>*hV0kII$sDEbyf< zf)`HS1{`S6GqIkG_3XZ1kM$f*!3HpB57d~ad^(W;T=sK83w?KqFE&d0br+K=Sqljr z0N1o5N6L+=SAR2->(IX_*;uXm9tN^w|K39gawK{gKJ;Y{!yE^k03v!S;I` zFYOLwijSNo7sM=*|4E28vQg}NI_Dwta=A4Ew-=VMOym_6E5N@7QW7i@FfLZ~16t~U zW`Jg58LJxzI`ddgx5+lvpaV%-hAB2UIpy7tcE5h~LHW^pyWi~Y?6RM83$MnP$~@3c zrywG3PmA1XP#v+2=n*i?IhiNv?c=|3j_C{s-flX}p_h62Gw33_m%nuGF!st2^gfJ$ z-^g&Qpn_;2ctrylD4w*_L0@E3#VUJPJf28G6xG?E#bPDJMSe8mogFE&K5U^@Wll8Xo;f;@nR?6_5iY>S3u4`(lBt$zXkH)g~D diff --git a/users/__pycache__/permissions.cpython-38.pyc b/users/__pycache__/permissions.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b4b213f2608e97b186a7f021791ab774935ac321 GIT binary patch literal 639 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 diff --git a/users/migrations/__pycache__/0002_rename_user_profile_userprofile_contacts.cpython-38.pyc b/users/migrations/__pycache__/0002_rename_user_profile_userprofile_contacts.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..165254ff8016cb36d2ef22c12426bee6af2d12de GIT binary patch literal 595 zcmYjNO-~y!5VhC4>E?q9l_NLAC6{c{Qx8?ep@LHp0&&U3+GZxH6KB1(H;s_E!IdAv zU$WFwPyGu>2*xH68EZ!KX6*5MbG^6MVPqfw9+nHv*uMaFEhTV5?v5w`1FqSUk2z;& z3=$Cc3f*whITBA<-xQ7* zFQ{*)+N@<+t0Jl{-WOYt{?F3JU*)dg2sYy7R{Nm7v4VKC$lX4LTqG}PmhvQ==kz6Z l&uK+%n3vVe4B%%J7VGa4-m(K4;PbC<+xZsey0C str: """ Hash value passed by user. @@ -31,9 +32,12 @@ def validate_password(self, value: str) -> str: """ return make_password(value) - def create(self, validated_data): - print(validated_data) - return User.objects.create_user(**validated_data) + # def create(self, validated_data): + # return super().create(validated_data) + + # def create(self, validated_data): + # print(validated_data) + # return User.objects.create_user(**validated_data) # def validate(self, data): # print(data) @@ -50,10 +54,18 @@ def create(self, validated_data): # return token +class UserProfileContactInfoSerializer(serializers.ModelSerializer): + class Meta: + model = UserProfileContactInfo + fields = ['website_link', 'twitter_link', 'github_link'] + + class UserProfileSerializer(serializers.ModelSerializer): + contacts = UserProfileContactInfoSerializer(many=False, read_only=True) # так мы добавляем сериализованный обьект в другой обьект + class Meta: model = UserProfile - exclude = ('id', ) + fields = ('logo', 'location', 'about', 'title', 'contacts') def create(self, validated_data): print(validated_data) diff --git a/users/urls.py b/users/urls.py index 54ac3b1..79e03d0 100644 --- a/users/urls.py +++ b/users/urls.py @@ -1,12 +1,13 @@ from django.urls import path, include from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView -from .views import GetUserAPIView, CreateUserAPIView, CreateUserProfileAPIView +from .views import GetUserAPIView, UserCreateAPIView, UserProfileDetailAPIView, TestClass urlpatterns = [ path('hello/', GetUserAPIView.as_view(), name='hello'), path('auth/', TokenObtainPairView.as_view(), name='auth'), path('refresh/', TokenRefreshView.as_view(), name='auth'), - path('register/', CreateUserAPIView.as_view(), name='reg'), - path('accounts/profile/', CreateUserProfileAPIView.as_view(), name='profile') + path('register/', UserCreateAPIView.as_view(), name='reg'), + path('/profile/', UserProfileDetailAPIView.as_view(), name='profile'), + path('test///', TestClass.as_view()), ] \ No newline at end of file diff --git a/users/utils.py b/users/utils.py new file mode 100644 index 0000000..b444a14 --- /dev/null +++ b/users/utils.py @@ -0,0 +1,11 @@ + +from rest_framework.views import exception_handler + +def user_exception_handler(exc, context): + # Call REST framework's default exception handler first, + # to get the standard error response. + response = exception_handler(exc, context) + + if response is not None: + response.data['status_code'] = response.status_code + return response diff --git a/users/views.py b/users/views.py index 7e8a76c..db54b5c 100644 --- a/users/views.py +++ b/users/views.py @@ -1,26 +1,29 @@ from django.contrib.auth import get_user_model -from rest_framework import generics, status +from rest_framework import generics, status, permissions # Create your views here. -from rest_framework.permissions import AllowAny, IsAuthenticated +from rest_framework.generics import get_object_or_404 + from rest_framework.response import Response +from rest_framework.views import APIView from rest_framework_simplejwt.authentication import JWTAuthentication - from users.models import UserProfile +from users.permissions import IsResourceOwner from users.serializers import CreateUserSerializer, UserProfileSerializer +User = get_user_model() class GetUserAPIView(generics.RetrieveAPIView): - permission_classes = [IsAuthenticated, ] - authentication_classes = [JWTAuthentication, ] + permission_classes = (permissions.IsAuthenticated, ) + authentication_classes = (JWTAuthentication, ) def get(self, request, *args, **kwargs): return Response('1') ## чтобы уж совсем не быть ленивым, я напишу свою регистрацию -class CreateUserAPIView(generics.CreateAPIView): - permission_classes = (AllowAny, ) +class UserCreateAPIView(generics.CreateAPIView): + permission_classes = (permissions.AllowAny, ) model = get_user_model() serializer_class = CreateUserSerializer @@ -34,15 +37,46 @@ def post(self, request, *args, **kwargs): return Response(serializer.data, status=status.HTTP_201_CREATED) -class CreateUserProfileAPIView(generics.CreateAPIView): - permission_classes = (IsAuthenticated, ) - authentication_classes = [JWTAuthentication, ] - model = UserProfile +class TestClass(generics.RetrieveAPIView): + multiple_lookup_fields = ('username', 'email', ) + # пример как сделать поиск по нескольким лукапам + # данный метод вызывается тогда, когда достается обьект по лукапам + # тут недоделанный вариант, пока нет в этом необходимости + def get_object(self): + queryset = self.get_queryset() + filter = {} + for field in self.multiple_lookup_fields: + filter[field] = self.kwargs[field] + obj = get_object_or_404(queryset, **filter) + self.check_object_permissions(self.request, obj) + return obj + +class UserProfileDetailAPIView(generics.RetrieveUpdateAPIView): + queryset = UserProfile.objects.all() + lookup_field = 'username' + permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsResourceOwner) ## данный пермиссионс требует авторизации только в тех методах запросов, которые изменяют данные + authentication_classes = (JWTAuthentication, ) serializer_class = UserProfileSerializer + detail = {'detail': 'User with that id doesn\'t exist'} + + def get(self, request, *args, **kwargs): + data = self.get_object() + serializer = self.serializer_class(data) + return Response(serializer.data, status=status.HTTP_200_OK) + + # def patch(self, request, *args, **kwargs): + # return super().patch(request, *args, **kwargs) + + def get_object(self) -> UserProfile: + user = User.objects.get(username=self.kwargs.get(self.lookup_field)) ## вот так достаем из lookup + obj = get_object_or_404(self.get_queryset(), user=user) + self.check_object_permissions(self.request, obj) + return obj + + + + - # def post(self, request, *args, **kwargs): - # print(request.user) - # return Response('2') From 8da1cbb0031b3f1a9318289929f7308fd8d364c7 Mon Sep 17 00:00:00 2001 From: whatislove118 Date: Mon, 30 Aug 2021 18:58:36 +0300 Subject: [PATCH 8/8] finish_users --- learn_docs/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 172 bytes learn_docs/__pycache__/admin.cpython-38.pyc | Bin 0 -> 213 bytes learn_docs/__pycache__/apps.cpython-38.pyc | Bin 0 -> 456 bytes learn_docs/__pycache__/mixins.cpython-38.pyc | Bin 0 -> 772 bytes learn_docs/__pycache__/models.cpython-38.pyc | Bin 0 -> 210 bytes .../__pycache__/serializers.cpython-38.pyc | Bin 0 -> 2081 bytes learn_docs/__pycache__/urls.cpython-38.pyc | Bin 0 -> 663 bytes learn_docs/__pycache__/views.cpython-38.pyc | Bin 0 -> 5180 bytes learn_docs/admin.py | 3 + learn_docs/apps.py | 6 + learn_docs/migrations/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 183 bytes learn_docs/mixins.py | 15 ++ learn_docs/models.py | 3 + learn_docs/serializers.py | 62 ++++++ learn_docs/tests.py | 3 + learn_docs/urls.py | 22 ++ learn_docs/views.py | 189 ++++++++++++++++++ .../__pycache__/settings.cpython-38.pyc | Bin 3757 -> 3773 bytes .../__pycache__/urls.cpython-38.pyc | Bin 1101 -> 1139 bytes stackoverflow_api/settings.py | 1 + stackoverflow_api/urls.py | 3 +- users/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 167 bytes users/__pycache__/admin.cpython-38.pyc | Bin 0 -> 208 bytes users/__pycache__/apps.cpython-38.pyc | Bin 0 -> 441 bytes users/__pycache__/models.cpython-38.pyc | Bin 0 -> 6256 bytes users/__pycache__/permissions.cpython-38.pyc | Bin 0 -> 941 bytes users/__pycache__/serializers.cpython-38.pyc | Bin 0 -> 2798 bytes users/__pycache__/urls.cpython-38.pyc | Bin 0 -> 735 bytes users/__pycache__/views.cpython-38.pyc | Bin 0 -> 3430 bytes users/models.py | 3 +- users/permissions.py | 9 + users/serializers.py | 86 +++++--- users/urls.py | 5 +- users/views.py | 52 +++-- 36 files changed, 413 insertions(+), 49 deletions(-) create mode 100644 learn_docs/__init__.py create mode 100644 learn_docs/__pycache__/__init__.cpython-38.pyc create mode 100644 learn_docs/__pycache__/admin.cpython-38.pyc create mode 100644 learn_docs/__pycache__/apps.cpython-38.pyc create mode 100644 learn_docs/__pycache__/mixins.cpython-38.pyc create mode 100644 learn_docs/__pycache__/models.cpython-38.pyc create mode 100644 learn_docs/__pycache__/serializers.cpython-38.pyc create mode 100644 learn_docs/__pycache__/urls.cpython-38.pyc create mode 100644 learn_docs/__pycache__/views.cpython-38.pyc create mode 100644 learn_docs/admin.py create mode 100644 learn_docs/apps.py create mode 100644 learn_docs/migrations/__init__.py create mode 100644 learn_docs/migrations/__pycache__/__init__.cpython-38.pyc create mode 100644 learn_docs/mixins.py create mode 100644 learn_docs/models.py create mode 100644 learn_docs/serializers.py create mode 100644 learn_docs/tests.py create mode 100644 learn_docs/urls.py create mode 100644 learn_docs/views.py create mode 100644 users/__pycache__/__init__.cpython-38.pyc create mode 100644 users/__pycache__/admin.cpython-38.pyc create mode 100644 users/__pycache__/apps.cpython-38.pyc create mode 100644 users/__pycache__/models.cpython-38.pyc create mode 100644 users/__pycache__/permissions.cpython-38.pyc create mode 100644 users/__pycache__/serializers.cpython-38.pyc create mode 100644 users/__pycache__/urls.cpython-38.pyc create mode 100644 users/__pycache__/views.cpython-38.pyc diff --git a/learn_docs/__init__.py b/learn_docs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/learn_docs/__pycache__/__init__.cpython-38.pyc b/learn_docs/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d3bc6890290ce62de6e5d4df0ff74f16719b7ae9 GIT binary patch literal 172 zcmWIL<>g`kf+j1aL=gQLL?8o3AjbiSi&=m~3PUi1CZpdd$xklUkB`sH%PfhH*DI*J#bJ}1pHiBWY6r6FGY~TX0DZeF(EtDd literal 0 HcmV?d00001 diff --git a/learn_docs/__pycache__/admin.cpython-38.pyc b/learn_docs/__pycache__/admin.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3a85eae8b62041dce5af1ea938ac48c64fbccd82 GIT binary patch literal 213 zcmYjKu?oU46iiwK5h{LzTbBkm5m5){B1o4mB{oshrb$VnrK^AAFLiZtc60KzlLvRl zy$5$m5KIu@v6|B@_^S{9@F47fb_T)_!xB|E#TYT~h(iAjR}|uIL|G+N9D3=L6;xex zlxyXTIGMiSiHr|6$Tud5<;IfcN}aG$-}z#(Bzta4r)zRBy5PC9S=|!rXkNnKoTY9u eS_>jMH7a8|w`3TM>b4uNE6r|_Z!k=JfanKU7dIOK literal 0 HcmV?d00001 diff --git a/learn_docs/__pycache__/apps.cpython-38.pyc b/learn_docs/__pycache__/apps.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e8c7a0bcb9264985fbcf0cecbd9e42438ee2d7c0 GIT binary patch literal 456 zcmYjN%}T>S5Z=wEQ2T=*UIp=%OG0lVBCVEs(IQAMxh!jDQ`2sbh|Br@_un9zVQ8V!M^wy^il088X!OwSd7m7h2)gGgfB&I;Ho5l>h*awCzetmc+MUfA6 zVmBmjComKFREO{sE;LAG!|QB1s7gJ`q=LGo9G(LqV;ck_(eXyO|ezw6n?fZ=_yKpR!CGG5P~U7k4hanR0xR*77~z9my?inoPDi(PJFd5grLhr zXBGz5X5=qN$4`&*5ZM;YwZ^@Vj)as5 zKolbA4WvvkM2r0XXr^mbY57t~EnXW_&Z?36q^3F9g6g0jp}s(MDVhduVFgQqGYPYT zC2in}p27P^ONJi1jT*Kh_F&^Tv|*=keNcBAQjmszCCdo!ck#qd6RjwLCR9kq!`Syj zb=Na#7f#mxZlQE7?Pg=3+lTTZJlgqgInV5c^9*mimwC zi0kTM$45@;!pHy8bk}Kor-k(l+5G|b9^(CelbkzgT{6GSYUQ-~D36X#k~8Vb+EmF~ zYeF9!uNH}`v%JK(Ewq{QtWt@VnVoWBa+gd3v+l53_}->{-9YLd8Xz%_i1bLG?4jNv zpP&4%J%^k7V{Bv0`81nI&V7&bi4ij${60s@GQE!2kfN<-%j|}|@c%-0U7#+e?Rq?9 q=>v;A@xmhT+}ILRPNh}3YbkC|Z6O7kY=HCe>G}cB!VhXO`|J;0=FFD> literal 0 HcmV?d00001 diff --git a/learn_docs/__pycache__/models.cpython-38.pyc b/learn_docs/__pycache__/models.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c75a04a68c76f4a207ebacc2838b77a2562e43f9 GIT binary patch literal 210 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 + # при неудачной валидации кидаем validation error + # только для полей required true + def validate_content(self, value): + if 'django' not in value.lower(): + raise serializers.ValidationError('This comment is not about Django') + return value + + # валидации на уровне обьекто + def validate(self, attrs): + # if attrs['start'] > attrs['finish']: + # raise serializers.ValidationError('finish ust occur after start') + return attrs + + # можно написать валидатор, который будет использоваться для нескольких полей + + def multiple_of_ten(self, value): + if value % 10 != 0: + raise serializers.ValidationError('Not a muliply of ten') + + class GameRecord(serializers.Serializer): + score = serializers.IntegerField( ) + + diff --git a/learn_docs/tests.py b/learn_docs/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/learn_docs/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/learn_docs/urls.py b/learn_docs/urls.py new file mode 100644 index 0000000..6c07443 --- /dev/null +++ b/learn_docs/urls.py @@ -0,0 +1,22 @@ +from django.urls import path +from rest_framework.routers import DefaultRouter + +from .views import TestRequestAPIView, TestList, UserViewSet, TestSerializers + +## 1 вариант +urlpatterns = [ + path('request/', TestRequestAPIView.as_view()), + path('list/', TestList.as_view()), + path('user/', UserViewSet.as_view({'get': 'list'})), + path('user//', UserViewSet.as_view({'get': 'retrieve'})), + path('serializer/', TestSerializers.as_view()) + + + + +] +## тк юрл самому писать тяжело и запарно, необходим роутер +## 2 вариант для вьюсетов +router = DefaultRouter() +router.register(r'user-set', UserViewSet, basename='user') +urlpatterns += router.urls diff --git a/learn_docs/views.py b/learn_docs/views.py new file mode 100644 index 0000000..74bae93 --- /dev/null +++ b/learn_docs/views.py @@ -0,0 +1,189 @@ +import datetime + +from django.contrib.auth import get_user_model +from django.shortcuts import render +from rest_framework import generics, permissions, status, viewsets + +# Create your views here. +from rest_framework.decorators import action +from rest_framework.generics import get_object_or_404 +from rest_framework.permissions import IsAuthenticated, AllowAny +from rest_framework.renderers import JSONRenderer +from rest_framework.response import Response +from rest_framework_simplejwt.authentication import JWTAuthentication + +from learn_docs.mixins import MultipleFieldLookupMixin +from learn_docs.serializers import Comment, CommentSerializer +from users.serializers import UserProfileSerializer, UserSerializer + +User = get_user_model() + +# добавляем миксин и все ок в целом + +class TestSerializers(generics.RetrieveUpdateDestroyAPIView): + serializer_class = CommentSerializer + authentication_classes = (JWTAuthentication, ) + + def get(self, request, *args, **kwargs): + comment = Comment(email='leila@example.com', content='foo bar') + # serializing + serializer = self.get_serializer(comment) + print(serializer.data) # здесь мы транслируем лишь в нативный питоновский словарь. Нужны рендеры + # json = JSONRenderer().render(serializer.data) + # print(json) + return Response(serializer.data) ## рендер вызывается автоматически + + def patch(self, request, *args, **kwargs): + serializer = self.get_serializer(data=request.data) ## нужно указывать дата + comment = None + if serializer.is_valid(raise_exception=True): + comment = serializer.save() # create + # comment = serializer.save(owner=request.user) # можно добавлять информацию при save, которая не находится в request.data + ## вызов create или update в методе save зависит от того, как мы обьявили сериализатор + # print(comment) + # serializer = self.get_serializer(data=request.data, instance=comment) + # if serializer.is_valid(): + # comment = serializer.save() # update + # print(comment) + return Response(serializer.validated_data) + + # def get_permissions(self): + # if self.request.method == 'GET': + # self.permission_classes = (AllowAny,) + # else: + # self.permission_classes = (IsAuthenticated, ) + # return super().get_permissions() + + # def get_serializer(self, *args, **kwargs): + # if self.request.method == 'GET': + # self.serializer_class = CommentSerializer + # else: + # self.serializer_class = UserSerializer + # return super().get_serializer(*args, **kwargs) + + +class TestList(generics.ListCreateAPIView): + queryset = User.objects.all() + serializer_class = UserSerializer + + def list(self, request, *args, **kwargs): + queryset = self.get_queryset() + serializer = self.serializer_class(queryset, many=True) ## флаг того, что много инстансов + return Response(serializer.data) + +class TestRequestAPIView(MultipleFieldLookupMixin, generics.RetrieveUpdateDestroyAPIView): + queryset = User.objects.all() + permission_classes = (permissions.AllowAny, ) + authentication_classes = (JWTAuthentication, ) + lookup_fields = ('id', 'username', ) + + def get(self, request, *args, **kwargs): + print(request.query_params) + return Response('1', status=status.HTTP_200_OK) + + def post(self, request, *args, **kwargs): + queryset = self.get_queryset() ## очень важно вызывать данный метод, чем обращаться к проперте напрямую, тк при обращении напрямую, он кэшируется и получается грустно невкусно + print(request.data) + print(request.accepted_renderer) + print(request.user) + print(request.auth) # тут крч содержится токен(или может что то другое), что является параметром для аутентификации + response = Response() + request.data['1'] = '2' + print(response.set_cookie('COKA', '12345', max_age=5)) + return response + + def handle_exception(self, exc): # вот так можно хэндлить ошибки любые(даже ерроры)(мб и не все) + print(type(exc)) + return Response(data={'detail': 'pizda', }) + + #def filter_queryset(self, queryset): фильтрует queryset (там нужны фильтры бэкэнды) + + # если нужно определить гибко сериализоатор, то как вариант. Если переопределяем, обращаемся только через метод(да и вообще всегда) + + def get_serializer_class(self): + if self.request.user.is_staff: + return UserProfileSerializer + return UserSerializer + + # хуки + # нужны для того, чтобы можно было переопределить сохранение или удаление внутри миксина(CreateModelMixin и тд вызывают их) + + # в данном случае очень удобно добавлять при сохранении информацию, которая передается вместе с request, но не с request.data + def perform_create(self, serializer): + serializer.save(user=self.request.user) + + # в данном случае можно вставлять определенную логику до или после сохранение + def perform_update(self, serializer): + #logger.log('update instance') + instance = serializer.save() ## сохранение изменненных данных + ##send_email_confirmation(user=self.request.user, modified=instance) + + ## также можно внедрять валидацию + # def perform_create(self, serializer): + # queryset = SignupRequest.objects.filter(user=self.request.user) + # if queryset.exists(): + # raise ValidationError('You have already signed up') + # serializer.save(user=self.request.user) + + +# если придется часто использовать данный миксин, можно создать базовый класс +class BaseRetrieveView(MultipleFieldLookupMixin, generics.RetrieveAPIView): + pass + +class BaseRetrieveUpdateDestroyView(MultipleFieldLookupMixin, generics.RetrieveUpdateDestroyAPIView): + pass + +### тесты классов +### В целом если мы переопределяем поведения методов, то настраивать некоторые параметры необязательно(если они нам в коде не нужны) +## но в целом тенденция такая, что для retrieve нам нужен полюбому queryset и model +### для create,update достаточно модели (и для delete тоже), однако в целом, иногда очень полезно вызывать queryset в коде через класс представления, а не через модель напрямую(например ели queryset отфильтрован) + + + +## view set +## обычный класс - не содежит действий +## GenericViewSet добавляет стандартную логику для вьюшек(действий также не содержит) +## ReadOnlyViewSet - list() и retrieve() +## ModelViewSet - содержит все методы(использовать можно, но для себя в крайне редких случаях) +## для всех них (кроме ModelViewSet) удобно использовать роутер +class UserViewSet(viewsets.ReadOnlyModelViewSet): + queryset = User.objects.all() + serializer_class = UserSerializer + authentication_classes = (JWTAuthentication, ) + ## так мы можем устанавливать permissions для конкретного юрл + + def get_permissions(self): + if self.action == 'list': + permission_classes = (IsAuthenticated,) + else: + permission_classes = (AllowAny, ) + return (permission() for permission in permission_classes) + + # благодаря данному декоратору можем создавать свои action. Можно настроить хоть пермиссион хоть что + @action(detail=True, methods=['post']) + def set_password(self, request, pk=None): + user = self.get_object() + serializer = UserSerializer(data=request.data) + if serializer.is_valid(): + user.set_password(serializer.validated_data['password']) + user.save() + return Response({'status': 'password set'}) + else: + return Response(serializer.errors, + status=status.HTTP_400_BAD_REQUEST) + + # def list(self, request, *args, **kwargs): + # queryset = User.objects.all() + # serializer = UserSerializer(queryset, many=True) + # return Response(serializer.data) + # + # def retrieve(self, request, pk=None): + # queryset = User.objects.all() + # user = get_object_or_404(queryset, pk=pk) + # serializer = UserSerializer(user) + # return Response(serializer.data) + + # у вью сета нету методов хендлеров вроде get post и тд + + + diff --git a/stackoverflow_api/__pycache__/settings.cpython-38.pyc b/stackoverflow_api/__pycache__/settings.cpython-38.pyc index 0fb7364e24f8486dfe654d889e5a08d546c8eb4b..97ca6d6c13ffb62eaf004caf81599839086b2907 100644 GIT binary patch delta 362 zcmYjM$xZ@M5S%v*7{?4M$~p}DB8xljxZti=l2Rv#b;o^aVe1I1@ zd-W?k`vbfdYz{7Ir-; z6!Kz*e3&IaTojNQlo{e9kF24gIY%buDa_fm^=zM8=&5pX^sis+sT8Y6P@*_@O1;z? z=mU@4FSVqOWlCU$l31k_)+mj2%5VoZP<~*NH*J3H2wKeCcum;e9( delta 347 zcmYjMxlY4S5cIQ65C#VV#5o*0r*n{ShWoyc8X=_ZB8~MF3JQK85~-1h(onb{A3zma zI=%wY@e65@c(xE?N2}S@?2NQ;){CWI>AEI~`uI*azB;1O)ScbPsvZ79Ls!9L;$Flg`bB}fQg5hhmnhs Ihmq$G0Bctl`v3p{ delta 89 zcmey&ah5|nl$V!_0SI0Q3n%t6GcY^`agYHUkmCTv#bFb*1G&@V}qSOx8 diff --git a/stackoverflow_api/settings.py b/stackoverflow_api/settings.py index 7d2f846..8fa2443 100644 --- a/stackoverflow_api/settings.py +++ b/stackoverflow_api/settings.py @@ -36,6 +36,7 @@ 'ckeditor', 'questions.apps.QuestionsConfig', 'users', + 'learn_docs', 'rest_framework', 'rest_framework_simplejwt', 'rest_framework_simplejwt.token_blacklist', diff --git a/stackoverflow_api/urls.py b/stackoverflow_api/urls.py index 12c5445..1653f4e 100644 --- a/stackoverflow_api/urls.py +++ b/stackoverflow_api/urls.py @@ -21,5 +21,6 @@ urlpatterns = [ path('admin/', admin.site.urls), path('admin-questions/', question_admin_site.urls), - path('', include('users.urls')) + path('', include('users.urls')), + path('test/', include('learn_docs.urls')), ] diff --git a/users/__pycache__/__init__.cpython-38.pyc b/users/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..00fd82d153afa4316bf4cfada63b66053dad1b23 GIT binary patch literal 167 zcmWIL<>g`kf=hb@6G8N25P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;!HIerR!OQL%n` zMq){3aZY|&s-dBUzDsIxc1eDLen3%vR%&udaeP6gesM`+ayC%DC@m+yJU+1?Q@<3f gGd?~uFS8^*Uaz3?7Kcr4eoARhsvXFd&p^xo01#Fxr2qf` literal 0 HcmV?d00001 diff --git a/users/__pycache__/admin.cpython-38.pyc b/users/__pycache__/admin.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7fca6cd711cebc4e28b78bf8b606ba39eb300a08 GIT binary patch literal 208 zcmWIL<>g`kf=hb@6YYWYV-N=!FabFZKwK;UBvKes7;_kM8KW2(8B&;n88n$+0!0}# z8E>&BrsQVk`Drpm@ug%X=B4NBCFkdr6lEqAfecv5P{a(Rz{D?4{m|mnqGJ8>jKq@6 z;+*`lR6|1xeV5eY?2`Nf{eYtUtkmR^;`o9}{o<0ut(H=GL literal 0 HcmV?d00001 diff --git a/users/__pycache__/apps.cpython-38.pyc b/users/__pycache__/apps.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5db2e9234336fb96c7c6648c2e319869b836a922 GIT binary patch literal 441 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 literal 0 HcmV?d00001 diff --git a/users/__pycache__/permissions.cpython-38.pyc b/users/__pycache__/permissions.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e912830265f269c0c2e67c79cb5f8a4ecdcbf55b GIT binary patch literal 941 zcmbVL&2G~`5Z?7}QWKJvRuCb?l|uxoRjCIK2({4ib0Db*l~67#*Sk^dI9YesZq%x` z_GR)gyue;L@d_L&X5v!ZsJ$@Kj%H>(f8V!0Y_%E$^d!3^k|~;a7i_>F!etF2wd)9%^*-sW{DMr9i{tSZpE z?DH55MFv!ofhVc-F3EsN26ebw`)t1|s|ypU-fXH&gv0$6v>BZDaO`tXIXR~Z&Ao(; z>)BJV`8iAY*t?*8GN;{n&G93xQ&*p;e59qp30$+k`(a;v+5h&bx7T;Pu*%H2<5s0Z zw_?Nvvj$>7!&$%o?Qq?gfYNr444IFpq7jPt?}d>tIjWajvuA!01FpxfR)I zVDm5i4wv!gqUbqq-ZyVI4;7pyfN!gd9hGMJ;E3QivU{$$@doie^J<8ns+yfbTQ5E zXYv^|yv1a@WU}PZ;83Q93ZL=v*tie!Q^Cx?I(v2Z4EI21Hf|TjdS(Gi8>|*qzXk>U yZl~r}4LlMtG=TO@n{oNDR^NqI9o+l?3j_^jRA#;@?ff?-LvJb8$0eI=btcPP6=?XJk4!JYr_YH@7w7gt#@cjMiljM(O z$N7gk^N)wl21Ne}B^<#rr^_P7=$&W0>qf49cQdc+N4|acvY=avO7`8)!frV#cPmka zIg%YY(b9L02t?_bBTCZWuO7M4GAyAe!%~K&2FnU86|n@%(uw5)ELE`#%kqikqNr^< zjg^;Jm(yUTwkL~tprwktxscflW|q6jfsFf!*27#0Oj*Kcnq=unDvj=yN4@m%KhyX5K53R`Elxowq3P{KL`Nfaouvq!TgWL|nSh zy<;cxPuk$5EeR(0Gbaj#D?GGi;bSv_xw_d&dVBKW$)dNX3vMwuojfjHe8`Uw{UOv- z_8in4vtRjB{+vB^_Bs0e7#8Nh;vPHHB7(bRJOas7dvZ9##xpKJFjWl~hKp8aZ4(>}6Sb!mrOyjTt)lt&r*(o#uMay(k?JgeBJbR}yZ)`z2Swgr z2cG+~Rp_{XxUPZX0mfB3%ZG8&PuFQxdL3%QP_uuiR^asmhz_9~R$;3wWTUsw1F1t{6GJ3DZ@#8^~jYkx*YRYh@?x!*nb_vm%l6{#J@jPXw znrC9ll?6&&!pC##B}x=6T9u)0K{SyS0z&-UFtGL(Os5`#YXhRMLru~cr_Uy=;72@i zg$w9BQ(t^Awx=BaKi|@gJn}BwJvZ`&2jBkay*K%8K9I=C>QL_Lw2*O@_6|(77^X#m z5@35vduh=b?Ap#&BT$#IA9aP)RZ^5v#<#`71Z_fFF9Oea6zCgF)>I2a<}pQBZZ*8u zJ(78m3v~kn8xWf>A+!B6*i0+!`+~4m{zb0{Y;<7Xff8nM-px&49nZ z^MV_%oAeG%S;<7ic`rLW6Om1p_139~#0LD^axuET(7y%0;Ttc@_i__vc?$P*?JB{+Y8ypH=BCJA2k5vg+-xQ)^KGhbD% z$Z`geDF1Ie- z%A!E{aBb-xt(|r~;en^ZgabsSGGfAM3HN0;Ni$QWp}0T3&r}oPOl7F(*ic^VyyUQK zHNw~O*?1IeMv|$@Uka7PCw1De6!qQ1I_TKe88TVlCz%QKPa?;3Ynv1^&({HC=DB`cd?Tw)*U*@2&SH;Ci z@t>AyN&BIVIi-;;p4-z>**r7(EFm^$)n^on65Y7EQ0gvqXD4S?^gqVHLx>KbLSE&y zi;8Spja9QI_VG6(Z_)u(>25O_fJs`Ixv^lRX%WGNNXNs}l+;Ds2CNcT9*nQ7Yp0Eq z-Q@U)!jCghH*K}m${v2=Reer_l^G27N-`@C=r7!y)t>O%^a1T)(RNMv)%Z&NnyQ*6 T7^=+v_yrzZTfMm&tXBR5u_2d8 literal 0 HcmV?d00001 diff --git a/users/__pycache__/urls.cpython-38.pyc b/users/__pycache__/urls.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9fc424cdd7b2c1fdfc07188fd457ae433aba988b GIT binary patch literal 735 zcmZ`%y^a$x5cd9Mck^@PK!~eoQG^uhI)qLq(47P{5CKQwCNl9PyNUNlwsYaOrREJN zpyWw-0V=mt_X<}r-t0+GU~9+zz8R0do$Gezi3s9vgYQK2MEoT{oIu#( zw8MRf{=oxCT0HFhBS>3((BV7Kclfiv>an31zax{wYg9-kz71<`G8^aRLT&{haqoS# zf%45q!}D^+bM+yIogKI2TUaX4nPaX!cRaKQFTh-AP|s(t+Q35x&J=J1x73lNW~y4| z63zkB%3IIw={{`YoSf5V3lhF zirN6T&4Uwd)YC2A=}w)}dVe!zakpz8{U>74s-bl!hrXVS>_DthVl~|=sTH=Ir~nOn zh;=aYrNSFf`DU*3qL#4Undv1KN2}09MrdrbCt^n=&bT+C+%vK<*K_B!1I&(Xz<((9 z52Eh62xo3yQdc`ocNsUimenO3A3vezpf{$fDb{oi3!~@t71f3>Ht1JNS?%V$&Z(<_ frcU>?zOu|dgNGA97feoGw=2K-uJzJcdFHrh3Dr#--%yqSk~Y4G5fgq zxQ3?wg5VZsiIuXDF?zQXJ9R=QbwfAxLeJ=_%h!MwXkkQT$DdH0f1S`N$R-A^iM zHLRNVLQ+fXVcon3Nh58B&9oJ^%y%(aN|(dsbR}F#SHo3i3HHzm&wXw25-&fpcv(38 zwTDi49-0cTLQ{q20yHl`Q{#1L>d;(-=0#{4ya`PcnwOxt#9KdD?WKQkF00LS?XJk8 zu@W*$hg>9WM+bMsK*(NKXVg!%jfKjz8_T`4glt(LFzIZ=mC|aRXlpA_$Aq*p;TSYm!f|OA%+{tnksI z6hafe5AZT!ed~ccvHEP{a`u=#@Frg0<~Da8Gx^5EJ93UKW@Rq4R_676?mo5=Y_9`C zxIE6{9lX?K+*2YFzjwum2=IhSTBKc&ggM>7mZosd{iB1PitYp896*T?R|1k5q8s1r z?A(gpyRsQwzx~zL&Q~|uzFZ2f z!BE9So4Jl8?KAOqpuC2rZa~b&`^Y=CLZ5@DBIp-IY1auX8zAk}g)2x}h#e|in$zxld#`a3I{oUDNHF%Nl(1=rS8b^{7KSMLL zu@J`+JG}V3(-Gg1gncQpu^e<6#+^LmWy0olH1!EY2I%)~5WqPii8C4+u*}2R{bLY> z+mG!BupJd(4<`1p&8!KX+yM#X70iLZxABJz0WG5)Ouy)#O&AC`251&rV+~eghp+yh zho)SJwX>pf(;LsBH5!HfTnZH}7lrfQ>V+-Mk@bn$6e-#4wpoE$u$eB1#6Y1M{ zcYq-G*g^tDjRkZ$ec#-hUPr!+u{-irNJ)ps7EH}DliUo6-lPt1kud%vcIlc)qsaC= zqHNrDXR;*$?@TgLy1Iil1xpQyvB3^6oOx`M9Oo!RE+-5a3ei*a>>^vHk(gO<0$dUgYN_=ydW$5>*llSf<-M$FkWixkIj#Ft@;Sq5UPge2=DR zJVDz48vyt1z?Wq0v@h!=-bYC_}Dsc2UsXQSfVlY~5zSIu_A zbMt1Tz`VrGbIqa^olPlgZeG2-FxJ#nvWNx3H|I@}>ZPMZ^!GEfv(uV4uZAjgtEhEz zJt|3QBy8rY*@R{i1bOzvcnWltU#ccu7(Y{Zbp5t~GsOLqlExHFx;j_pm`hc!obTWkLTsB0MG literal 0 HcmV?d00001 diff --git a/users/models.py b/users/models.py index 507d205..16f24d5 100644 --- a/users/models.py +++ b/users/models.py @@ -118,7 +118,6 @@ def get_short_name(self): def _create_user_profile(self): """ Создает и инициализирует профиль юзера вместе с контактной информацией""" - print(self) user_profile = UserProfile() user_profile.user = self user_contact_info = UserProfileContactInfo() @@ -154,7 +153,7 @@ class UserProfileContactInfo(models.Model): class UserProfile(models.Model): - user = models.OneToOneField(User, primary_key=True, to_field='id', on_delete=models.CASCADE) + user = models.OneToOneField(User, primary_key=True, related_name='user_profile', to_field='id', on_delete=models.CASCADE) contacts = models.OneToOneField(UserProfileContactInfo, blank=False, on_delete=models.CASCADE) logo = models.ImageField(blank=False, null=False, upload_to=settings.CUSTOM_USER_PROFILE_LOGO.format(id), diff --git a/users/permissions.py b/users/permissions.py index dd3568f..134a3b3 100644 --- a/users/permissions.py +++ b/users/permissions.py @@ -8,3 +8,12 @@ def has_object_permission(self, request, view, obj): request.method in permissions.SAFE_METHODS or request.user == obj.user ) + + +class IsResourceOwnerAccount(permissions.BasePermission): + + def has_object_permission(self, request, view, obj): + return bool( + request.method in permissions.SAFE_METHODS + or request.user == obj + ) diff --git a/users/serializers.py b/users/serializers.py index 22bdf0d..629056b 100644 --- a/users/serializers.py +++ b/users/serializers.py @@ -8,29 +8,6 @@ User = get_user_model() -class CreateUserSerializer(serializers.ModelSerializer): - # username = serializers.CharField(validators=[UniqueValidator(queryset=User.objects.all())]) - - class Meta: - model = User - fields = ('username', 'password', 'email') - # extra_kwargs = {'email': {'required': True}} - # validators = [ - # UniqueTogetherValidator( - # queryset=User.objects.all(), - # fields=('username', 'email') - # ) - # ] - - - def validate_password(self, value: str) -> str: - """ - Hash value passed by user. - - :param value: password of a user - :return: a hashed version of the password - """ - return make_password(value) # def create(self, validated_data): # return super().create(validated_data) @@ -53,6 +30,36 @@ def validate_password(self, value: str) -> str: # print(token) # return token +class ChangePasswordSerializer(serializers.Serializer): + new_password = serializers.CharField() + repeat_new_password = serializers.CharField() + old_password = serializers.CharField() + + """ Здесь мы будем хранить логику по изменению пароля""" + def save(self, **kwargs): + user = kwargs.get('user') + user.set_password(self.validated_data.get('new_password')) + user.save() + return user + + def validate(self, attrs): + new_password = attrs.get('new_password') + + + # def save(self, **kwargs): + # user = kwargs.get('user') + # print(user) + # old_password = kwargs.get('password') + # print(old_password) + # new_password = kwargs.get('new_password') + # user.set_password(new_password) + # user.save() + + # def validate_old_password(self, value: str) -> str: + # if value == self.old_password: + # raise serializers.ValidationError('Пароли совпадают') + # return super().validate_password(value) + class UserProfileContactInfoSerializer(serializers.ModelSerializer): class Meta: @@ -67,9 +74,36 @@ class Meta: model = UserProfile fields = ('logo', 'location', 'about', 'title', 'contacts') - def create(self, validated_data): - print(validated_data) - return super().create(validated_data) + # def create(self, validated_data): + # return super().create(validated_data) +class UserSerializer(serializers.ModelSerializer): + # username = serializers.CharField(validators=[UniqueValidator(queryset=User.objects.all())]) + user_profile = UserProfileSerializer(many=False, read_only=True) + class Meta: + model = User + fields = ('id', 'username', 'password', 'email', 'user_profile') + extra_kwargs = { + 'password': { + 'write_only': True + } + } + depth = 2 + # validators = [ + # UniqueTogetherValidator( + # queryset=User.objects.all(), + # fields=('username', 'email') + # ) + # ] + + + def validate_password(self, value: str) -> str: + """ + Hash value passed by user. + + :param value: password of a user + :return: a hashed version of the password + """ + return make_password(value) diff --git a/users/urls.py b/users/urls.py index 79e03d0..c95464f 100644 --- a/users/urls.py +++ b/users/urls.py @@ -1,7 +1,7 @@ from django.urls import path, include from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView -from .views import GetUserAPIView, UserCreateAPIView, UserProfileDetailAPIView, TestClass +from .views import GetUserAPIView, UserCreateAPIView, UserProfileDetailAPIView, ChangePasswordAPIView urlpatterns = [ path('hello/', GetUserAPIView.as_view(), name='hello'), @@ -9,5 +9,6 @@ path('refresh/', TokenRefreshView.as_view(), name='auth'), path('register/', UserCreateAPIView.as_view(), name='reg'), path('/profile/', UserProfileDetailAPIView.as_view(), name='profile'), - path('test///', TestClass.as_view()), + path('/profile/change/password/', ChangePasswordAPIView.as_view(),) + # path('test///', TestLookup.as_view()), ] \ No newline at end of file diff --git a/users/views.py b/users/views.py index db54b5c..3e2c249 100644 --- a/users/views.py +++ b/users/views.py @@ -8,8 +8,8 @@ from rest_framework_simplejwt.authentication import JWTAuthentication from users.models import UserProfile -from users.permissions import IsResourceOwner -from users.serializers import CreateUserSerializer, UserProfileSerializer +from users.permissions import IsResourceOwner, IsResourceOwnerAccount +from users.serializers import UserSerializer, UserProfileSerializer, ChangePasswordSerializer User = get_user_model() @@ -25,11 +25,10 @@ def get(self, request, *args, **kwargs): class UserCreateAPIView(generics.CreateAPIView): permission_classes = (permissions.AllowAny, ) model = get_user_model() - serializer_class = CreateUserSerializer + serializer_class = UserSerializer ## этого можно было и не делать, но чисто для себя я написал def post(self, request, *args, **kwargs): - print(request.user) user = request.data serializer = self.serializer_class(data=user) if serializer.is_valid(raise_exception=True): @@ -37,19 +36,19 @@ def post(self, request, *args, **kwargs): return Response(serializer.data, status=status.HTTP_201_CREATED) -class TestClass(generics.RetrieveAPIView): - multiple_lookup_fields = ('username', 'email', ) - # пример как сделать поиск по нескольким лукапам - # данный метод вызывается тогда, когда достается обьект по лукапам - # тут недоделанный вариант, пока нет в этом необходимости - def get_object(self): - queryset = self.get_queryset() - filter = {} - for field in self.multiple_lookup_fields: - filter[field] = self.kwargs[field] - obj = get_object_or_404(queryset, **filter) - self.check_object_permissions(self.request, obj) - return obj +# class TestLookup(generics.RetrieveAPIView): +# multiple_lookup_fields = ('username', 'email', ) +# # пример как сделать поиск по нескольким лукапам +# # данный метод вызывается тогда, когда достается обьект по лукапам +# # тут недоделанный вариант, пока нет в этом необходимости +# def get_object(self): +# queryset = self.get_queryset() +# filter = {} +# for field in self.multiple_lookup_fields: +# filter[field] = self.kwargs[field] +# obj = get_object_or_404(queryset, **filter) +# self.check_object_permissions(self.request, obj) +# return obj class UserProfileDetailAPIView(generics.RetrieveUpdateAPIView): queryset = UserProfile.objects.all() @@ -57,7 +56,6 @@ class UserProfileDetailAPIView(generics.RetrieveUpdateAPIView): permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsResourceOwner) ## данный пермиссионс требует авторизации только в тех методах запросов, которые изменяют данные authentication_classes = (JWTAuthentication, ) serializer_class = UserProfileSerializer - detail = {'detail': 'User with that id doesn\'t exist'} def get(self, request, *args, **kwargs): data = self.get_object() @@ -76,6 +74,24 @@ def get_object(self) -> UserProfile: +class ChangePasswordAPIView(generics.UpdateAPIView): + queryset = User.objects.all() + permission_classes = (permissions.IsAuthenticated, IsResourceOwnerAccount) + authentication_classes = (JWTAuthentication, ) + serializer_class = ChangePasswordSerializer + lookup_field = 'username' + model = get_user_model() + + def post(self, request, **kwargs): + serializer = self.get_serializer(data=request.data) + if serializer.is_valid(raise_exception=True): + serializer.save(user=self.get_object()) + return Response(serializer.validated_data) + + def get_object(self): + obj = get_object_or_404(self.get_queryset(), username=self.kwargs.get(self.lookup_field)) + self.check_object_permissions(self.request, obj) + return obj