diff --git a/akarpov/blog/migrations/0001_initial.py b/akarpov/blog/migrations/0001_initial.py index e2ba852..c215ae4 100644 --- a/akarpov/blog/migrations/0001_initial.py +++ b/akarpov/blog/migrations/0001_initial.py @@ -1,12 +1,13 @@ # Generated by Django 4.0.8 on 2022-11-23 08:30 -import akarpov.utils.files import ckeditor_uploader.fields import colorfield.fields from django.conf import settings from django.db import migrations, models import django.db.models.deletion +import akarpov.utils.files + class Migration(migrations.Migration): @@ -18,73 +19,184 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='Tag', + name="Tag", fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=20, unique=True)), - ('color', colorfield.fields.ColorField(blank=True, default='#FF0000', image_field=None, max_length=18, samples=None)), + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=20, unique=True)), + ( + "color", + colorfield.fields.ColorField( + blank=True, + default="#FF0000", + image_field=None, + max_length=18, + samples=None, + ), + ), ], ), migrations.CreateModel( - name='Post', + name="Post", fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('title', models.CharField(max_length=100)), - ('body', ckeditor_uploader.fields.RichTextUploadingField()), - ('slug', models.SlugField(blank=True, max_length=20)), - ('post_views', models.IntegerField(default=0)), - ('rating', models.IntegerField(default=0)), - ('rating_up', models.IntegerField(default=0)), - ('rating_down', models.IntegerField(default=0)), - ('comment_count', models.IntegerField(default=0)), - ('created', models.DateTimeField(auto_now_add=True)), - ('edited', models.DateTimeField(auto_now=True)), - ('image', models.ImageField(blank=True, upload_to=akarpov.utils.files.user_file_upload_mixin)), - ('image_cropped', models.ImageField(blank=True, upload_to='cropped/')), - ('creator', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='posts', to=settings.AUTH_USER_MODEL)), - ('tags', models.ManyToManyField(related_name='posts', to='blog.tag')), + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("title", models.CharField(max_length=100)), + ("body", ckeditor_uploader.fields.RichTextUploadingField()), + ("slug", models.SlugField(blank=True, max_length=20)), + ("post_views", models.IntegerField(default=0)), + ("rating", models.IntegerField(default=0)), + ("rating_up", models.IntegerField(default=0)), + ("rating_down", models.IntegerField(default=0)), + ("comment_count", models.IntegerField(default=0)), + ("created", models.DateTimeField(auto_now_add=True)), + ("edited", models.DateTimeField(auto_now=True)), + ( + "image", + models.ImageField( + blank=True, upload_to=akarpov.utils.files.user_file_upload_mixin + ), + ), + ("image_cropped", models.ImageField(blank=True, upload_to="cropped/")), + ( + "creator", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="posts", + to=settings.AUTH_USER_MODEL, + ), + ), + ("tags", models.ManyToManyField(related_name="posts", to="blog.tag")), ], options={ - 'ordering': ['-created'], + "ordering": ["-created"], }, ), migrations.CreateModel( - name='Comment', + name="Comment", fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('body', models.CharField(max_length=500)), - ('created', models.DateTimeField(auto_now_add=True)), - ('rating', models.IntegerField(default=0)), - ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to=settings.AUTH_USER_MODEL)), - ('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='blog.comment')), - ('post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='blog.post')), + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("body", models.CharField(max_length=500)), + ("created", models.DateTimeField(auto_now_add=True)), + ("rating", models.IntegerField(default=0)), + ( + "author", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="comments", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "parent", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="blog.comment", + ), + ), + ( + "post", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="comments", + to="blog.post", + ), + ), ], options={ - 'ordering': ['-rating'], + "ordering": ["-rating"], }, ), migrations.CreateModel( - name='PostRating', + name="PostRating", fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('vote_up', models.BooleanField()), - ('post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ratings', to='blog.post')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='post_ratings', to=settings.AUTH_USER_MODEL)), + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("vote_up", models.BooleanField()), + ( + "post", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="ratings", + to="blog.post", + ), + ), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="post_ratings", + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ - 'unique_together': {('user', 'post')}, + "unique_together": {("user", "post")}, }, ), migrations.CreateModel( - name='CommentRating', + name="CommentRating", fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('vote_up', models.BooleanField()), - ('comment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ratings', to='blog.comment')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comment_ratings', to=settings.AUTH_USER_MODEL)), + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("vote_up", models.BooleanField()), + ( + "comment", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="ratings", + to="blog.comment", + ), + ), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="comment_ratings", + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ - 'unique_together': {('comment', 'user')}, + "unique_together": {("comment", "user")}, }, ), ] diff --git a/akarpov/blog/migrations/0002_alter_comment_options.py b/akarpov/blog/migrations/0002_alter_comment_options.py index b4aa79f..d1ff841 100644 --- a/akarpov/blog/migrations/0002_alter_comment_options.py +++ b/akarpov/blog/migrations/0002_alter_comment_options.py @@ -6,12 +6,12 @@ class Migration(migrations.Migration): dependencies = [ - ('blog', '0001_initial'), + ("blog", "0001_initial"), ] operations = [ migrations.AlterModelOptions( - name='comment', - options={'ordering': ['-rating', '-created']}, + name="comment", + options={"ordering": ["-rating", "-created"]}, ), ] diff --git a/akarpov/blog/models.py b/akarpov/blog/models.py index 9dcae76..b95844c 100644 --- a/akarpov/blog/models.py +++ b/akarpov/blog/models.py @@ -6,7 +6,7 @@ from akarpov.users.models import User from akarpov.utils.files import user_file_upload_mixin -from utils.string import cleanhtml +from akarpov.utils.string import cleanhtml class Post(models.Model): diff --git a/akarpov/contrib/sites/migrations/0003_set_site_domain_and_name.py b/akarpov/contrib/sites/migrations/0003_set_site_domain_and_name.py index 53b8cf7..6d2c1f5 100644 --- a/akarpov/contrib/sites/migrations/0003_set_site_domain_and_name.py +++ b/akarpov/contrib/sites/migrations/0003_set_site_domain_and_name.py @@ -23,7 +23,7 @@ def _update_or_create_site_with_sequence(site_model, connection, domain, name): # site is created. # To avoid this, we need to manually update DB sequence and make sure it's # greater than the maximum value. - max_id = site_model.objects.order_by('-id').first().id + max_id = site_model.objects.order_by("-id").first().id with connection.cursor() as cursor: cursor.execute("SELECT last_value from django_site_id_seq") (current_id,) = cursor.fetchone() diff --git a/akarpov/pipeliner/migrations/0001_initial.py b/akarpov/pipeliner/migrations/0001_initial.py index 4d66d3a..1286a4e 100644 --- a/akarpov/pipeliner/migrations/0001_initial.py +++ b/akarpov/pipeliner/migrations/0001_initial.py @@ -11,29 +11,68 @@ class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('contenttypes', '0002_remove_content_type_name'), + ("contenttypes", "0002_remove_content_type_name"), ] operations = [ migrations.CreateModel( - name='Workspace', + name="Workspace", fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(blank=True, max_length=50)), - ('slug', models.SlugField(max_length=8)), + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(blank=True, max_length=50)), + ("slug", models.SlugField(max_length=8)), ], ), migrations.CreateModel( - name='BaseBlock', + name="BaseBlock", fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('creator', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pipeline_blocks', to=settings.AUTH_USER_MODEL)), - ('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='blocks', to='pipeliner.workspace')), + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "creator", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="pipeline_blocks", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "polymorphic_ctype", + models.ForeignKey( + editable=False, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="polymorphic_%(app_label)s.%(class)s_set+", + to="contenttypes.contenttype", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="blocks", + to="pipeliner.workspace", + ), + ), ], options={ - 'abstract': False, - 'base_manager_name': 'objects', + "abstract": False, + "base_manager_name": "objects", }, ), ] diff --git a/akarpov/tools/qr/api/serializers.py b/akarpov/tools/qr/api/serializers.py index 2768675..9602cd6 100644 --- a/akarpov/tools/qr/api/serializers.py +++ b/akarpov/tools/qr/api/serializers.py @@ -15,6 +15,10 @@ class Meta: } def create(self, validated_data): - user = self.context["request"].user.is_authenticated if self.context["request"].user else None + user = ( + self.context["request"].user.is_authenticated + if self.context["request"].user + else None + ) qr = simple.run(words=validated_data["body"], user=user) return qr diff --git a/akarpov/tools/qr/api/views.py b/akarpov/tools/qr/api/views.py index ebb68de..81c21f6 100644 --- a/akarpov/tools/qr/api/views.py +++ b/akarpov/tools/qr/api/views.py @@ -1,10 +1,10 @@ from rest_framework import generics from rest_framework.generics import get_object_or_404 -from rest_framework.viewsets import GenericViewSet from rest_framework.permissions import AllowAny +from rest_framework.viewsets import GenericViewSet +from akarpov.tools.qr.api.serializers import QRSerializer from akarpov.tools.qr.models import QR -from .serializers import QRSerializer class QRViewSet(generics.ListCreateAPIView, generics.RetrieveAPIView, GenericViewSet): diff --git a/akarpov/tools/qr/services/simple.py b/akarpov/tools/qr/services/simple.py index 68e3bf3..c8e6c77 100644 --- a/akarpov/tools/qr/services/simple.py +++ b/akarpov/tools/qr/services/simple.py @@ -4,8 +4,8 @@ from django.core.files import File from akarpov.tools.qr.models import QR -from akarpov.utils.generators import generate_charset from akarpov.users.models import User +from akarpov.utils.generators import generate_charset def run(words: str, path: str = "/tmp/", user: User = None) -> QR: diff --git a/akarpov/tools/urls.py b/akarpov/tools/urls.py index e5a1656..2cf1eb8 100644 --- a/akarpov/tools/urls.py +++ b/akarpov/tools/urls.py @@ -1,4 +1,4 @@ -from django.urls import path, include +from django.urls import include, path app_name = "tools" urlpatterns = [path("qr/", include("akarpov.tools.qr.urls", namespace="qr"))] diff --git a/akarpov/users/api/urls.py b/akarpov/users/api/urls.py index 1347c3a..e1606ab 100644 --- a/akarpov/users/api/urls.py +++ b/akarpov/users/api/urls.py @@ -1,4 +1,5 @@ from django.urls import path + from .views import ( UserListViewSet, UserRetireUpdateSelfViewSet, diff --git a/akarpov/users/migrations/0001_initial.py b/akarpov/users/migrations/0001_initial.py index 35000df..07f9b1d 100644 --- a/akarpov/users/migrations/0001_initial.py +++ b/akarpov/users/migrations/0001_initial.py @@ -1,47 +1,143 @@ # Generated by Django 4.0.8 on 2022-11-23 08:11 -import akarpov.utils.files import django.contrib.auth.models import django.contrib.auth.validators from django.db import migrations, models import django.utils.timezone +import akarpov.utils.files + class Migration(migrations.Migration): initial = True dependencies = [ - ('auth', '0012_alter_user_first_name_max_length'), + ("auth", "0012_alter_user_first_name_max_length"), ] operations = [ migrations.CreateModel( - name='User', + 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')), - ('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')), - ('name', models.CharField(blank=True, max_length=255, verbose_name='Name of User')), - ('about', models.TextField(blank=True, max_length=100, verbose_name='Description')), - ('image', models.ImageField(blank=True, upload_to=akarpov.utils.files.user_file_upload_mixin)), - ('image_cropped', models.ImageField(blank=True, upload_to='cropped/')), - ('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')), + ( + "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", + ), + ), + ( + "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" + ), + ), + ( + "name", + models.CharField( + blank=True, max_length=255, verbose_name="Name of User" + ), + ), + ( + "about", + models.TextField( + blank=True, max_length=100, verbose_name="Description" + ), + ), + ( + "image", + models.ImageField( + blank=True, upload_to=akarpov.utils.files.user_file_upload_mixin + ), + ), + ("image_cropped", models.ImageField(blank=True, upload_to="cropped/")), + ( + "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, + "verbose_name": "user", + "verbose_name_plural": "users", + "abstract": False, }, managers=[ - ('objects', django.contrib.auth.models.UserManager()), + ("objects", django.contrib.auth.models.UserManager()), ], ), ] diff --git a/akarpov/users/tests/test_models.py b/akarpov/users/tests/test_models.py index aad162e..3fe97c1 100644 --- a/akarpov/users/tests/test_models.py +++ b/akarpov/users/tests/test_models.py @@ -1,17 +1,7 @@ -import pytest - -pytestmark = pytest.mark.django_db +from akarpov.users.models import User -class TestUser: - @pytest.fixture - def user_with_code(self, user_factory): - return user_factory(user_code="1234") - - def test_send_code(self, mailoutbox, user_factory): - user_with_code.send_code() - - assert len(mailoutbox) == 1 - m = mailoutbox[0] - assert list(m.to) == [user_with_code.email] - assert "1234" in m.body +def test_user_create(user: User): + password = "123" + user.set_password(password) + assert user.check_password(password)