From 72e80817a5e5b2db0e61ce7b2a34dd321757f434 Mon Sep 17 00:00:00 2001 From: Maximilian Friedersdorff Date: Fri, 8 Jul 2022 21:49:42 +0100 Subject: [PATCH 1/4] Fix null=True vs blank=True on string fields Django documentation suggests no using null=True, but instead defaulting to the empty string. It's necessary of course to allow blank=True so that users don't have to enter the field --- .../0003_alter_post_body_alter_post_title.py | 23 +++++++++++++++++++ flangr/posts/models.py | 4 ++-- 2 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 flangr/posts/migrations/0003_alter_post_body_alter_post_title.py diff --git a/flangr/posts/migrations/0003_alter_post_body_alter_post_title.py b/flangr/posts/migrations/0003_alter_post_body_alter_post_title.py new file mode 100644 index 0000000..64da38b --- /dev/null +++ b/flangr/posts/migrations/0003_alter_post_body_alter_post_title.py @@ -0,0 +1,23 @@ +# Generated by Django 4.0.5 on 2022-07-08 20:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("posts", "0002_alter_comment_post_alter_post_img"), + ] + + operations = [ + migrations.AlterField( + model_name="post", + name="body", + field=models.TextField(blank=True, default=""), + ), + migrations.AlterField( + model_name="post", + name="title", + field=models.CharField(blank=True, default="", max_length=255), + ), + ] diff --git a/flangr/posts/models.py b/flangr/posts/models.py index fa22ba2..ad1786e 100644 --- a/flangr/posts/models.py +++ b/flangr/posts/models.py @@ -17,8 +17,8 @@ class Post(models.Model): posted = models.DateTimeField(auto_now_add=True) public = models.BooleanField(default=False) - title = models.CharField(max_length=255, null=True) - body = models.TextField(null=True) + title = models.CharField(max_length=255, blank=True, default="") + body = models.TextField(blank=True, default="") user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) From ce230f11014e49b911536a7240b1bad11a380ec9 Mon Sep 17 00:00:00 2001 From: Maximilian Friedersdorff Date: Fri, 8 Jul 2022 21:50:39 +0100 Subject: [PATCH 2/4] Add circles app with Circle model --- config/settings/base.py | 1 + flangr/circles/__init__.py | 0 flangr/circles/admin.py | 3 ++ flangr/circles/apps.py | 6 +++ flangr/circles/migrations/0001_initial.py | 45 +++++++++++++++++++++++ flangr/circles/migrations/__init__.py | 0 flangr/circles/models.py | 16 ++++++++ flangr/circles/tests.py | 3 ++ flangr/circles/views.py | 3 ++ 9 files changed, 77 insertions(+) create mode 100644 flangr/circles/__init__.py create mode 100644 flangr/circles/admin.py create mode 100644 flangr/circles/apps.py create mode 100644 flangr/circles/migrations/0001_initial.py create mode 100644 flangr/circles/migrations/__init__.py create mode 100644 flangr/circles/models.py create mode 100644 flangr/circles/tests.py create mode 100644 flangr/circles/views.py diff --git a/config/settings/base.py b/config/settings/base.py index 0439961..c713143 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -36,6 +36,7 @@ INSTALLED_APPS = [ "django.contrib.messages", "django.contrib.staticfiles", "flangr.posts", + "flangr.circles", ] MIDDLEWARE = [ diff --git a/flangr/circles/__init__.py b/flangr/circles/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/flangr/circles/admin.py b/flangr/circles/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/flangr/circles/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/flangr/circles/apps.py b/flangr/circles/apps.py new file mode 100644 index 0000000..7ea759c --- /dev/null +++ b/flangr/circles/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class CirclesConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "flangr.circles" diff --git a/flangr/circles/migrations/0001_initial.py b/flangr/circles/migrations/0001_initial.py new file mode 100644 index 0000000..71f47f9 --- /dev/null +++ b/flangr/circles/migrations/0001_initial.py @@ -0,0 +1,45 @@ +# Generated by Django 4.0.5 on 2022-07-08 20:49 + +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="Circle", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=255)), + ( + "description", + models.CharField(blank=True, default="", max_length=255), + ), + ("members", models.ManyToManyField(to=settings.AUTH_USER_MODEL)), + ( + "owner", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="owned_circles", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + ), + ] diff --git a/flangr/circles/migrations/__init__.py b/flangr/circles/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/flangr/circles/models.py b/flangr/circles/models.py new file mode 100644 index 0000000..8b5eb31 --- /dev/null +++ b/flangr/circles/models.py @@ -0,0 +1,16 @@ +from django.db import models + +from django.conf import settings + +# Create your models here. + + +class Circle(models.Model): + name = models.CharField(max_length=255) + description = models.CharField(max_length=255, blank=True, default="") + + members = models.ManyToManyField(settings.AUTH_USER_MODEL) + + owner = models.ForeignKey( + settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="owned_circles" + ) diff --git a/flangr/circles/tests.py b/flangr/circles/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/flangr/circles/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/flangr/circles/views.py b/flangr/circles/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/flangr/circles/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. From ac962034dffd88c0582972fd8f43824403639fcd Mon Sep 17 00:00:00 2001 From: Maximilian Friedersdorff Date: Sat, 9 Jul 2022 14:29:41 +0100 Subject: [PATCH 3/4] Use zimports to sort imports --- .pre-commit-config.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c7e49cc..4b8bc1c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,3 +9,7 @@ repos: rev: 22.6.0 hooks: - id: black +- repo: https://github.com/sqlalchemyorg/zimports/ + rev: v0.4.5 + hooks: + - id: zimports From 2cb910cc30410f345fa5fb2fe15d2f691abb2109 Mon Sep 17 00:00:00 2001 From: Maximilian Friedersdorff Date: Sat, 9 Jul 2022 14:30:24 +0100 Subject: [PATCH 4/4] Add circles, views to modify them and ability to add them to posts --- flangr/circles/admin.py | 4 ++ flangr/circles/models.py | 6 ++- flangr/circles/urls.py | 10 +++++ flangr/circles/views.py | 40 ++++++++++++++++++- ...004_remove_post_public_post_shared_with.py | 24 +++++++++++ .../0005_rename_shared_with_post_circles.py | 18 +++++++++ flangr/posts/models.py | 6 +-- flangr/posts/views.py | 15 ++++--- flangr/templates/circles/circle_form.html | 9 +++++ flangr/templates/circles/circle_list.html | 9 +++++ flangr/templates/posts/post_form.html | 2 +- flangr/urls.py | 7 ++-- 12 files changed, 134 insertions(+), 16 deletions(-) create mode 100644 flangr/circles/urls.py create mode 100644 flangr/posts/migrations/0004_remove_post_public_post_shared_with.py create mode 100644 flangr/posts/migrations/0005_rename_shared_with_post_circles.py create mode 100644 flangr/templates/circles/circle_form.html create mode 100644 flangr/templates/circles/circle_list.html diff --git a/flangr/circles/admin.py b/flangr/circles/admin.py index 8c38f3f..0cd1f08 100644 --- a/flangr/circles/admin.py +++ b/flangr/circles/admin.py @@ -1,3 +1,7 @@ from django.contrib import admin +from .models import Circle + # Register your models here. + +admin.site.register(Circle) diff --git a/flangr/circles/models.py b/flangr/circles/models.py index 8b5eb31..77d818a 100644 --- a/flangr/circles/models.py +++ b/flangr/circles/models.py @@ -1,6 +1,5 @@ -from django.db import models - from django.conf import settings +from django.db import models # Create your models here. @@ -14,3 +13,6 @@ class Circle(models.Model): owner = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="owned_circles" ) + + def __str__(self): + return f"Circle: {self.name} of user {self.owner_id}" diff --git a/flangr/circles/urls.py b/flangr/circles/urls.py new file mode 100644 index 0000000..54d0d59 --- /dev/null +++ b/flangr/circles/urls.py @@ -0,0 +1,10 @@ +from django.urls import path + +from . import views + +app_name = "circles" +urlpatterns = [ + path("", views.CircleListView.as_view(), name="circle_list"), + path("circle/", views.CircleUpdateView.as_view(), name="circle_update"), + path("add", views.CircleCreateView.as_view(), name="circle_create"), +] diff --git a/flangr/circles/views.py b/flangr/circles/views.py index 91ea44a..590f5ed 100644 --- a/flangr/circles/views.py +++ b/flangr/circles/views.py @@ -1,3 +1,41 @@ -from django.shortcuts import render +from django.contrib.auth.mixins import LoginRequiredMixin +from django.http import HttpResponseRedirect +from django.urls import reverse +from django.views.generic import CreateView +from django.views.generic import ListView +from django.views.generic import UpdateView + +from .models import Circle # Create your views here. + + +class CircleListView(LoginRequiredMixin, ListView): + def get_queryset(self): + return Circle.objects.filter(owner=self.request.user) + + +class CircleUpdateView(LoginRequiredMixin, UpdateView): + model = Circle + fields = ["name", "description", "members"] + + def get_queryset(self): + return Circle.objects.filter(owner=self.request.user) + + def get_success_url(self): + return reverse("circles:circle_list") + + +class CircleCreateView(LoginRequiredMixin, CreateView): + model = Circle + fields = ["name", "description", "members"] + + def form_valid(self, form): + circle = form.save(commit=False) + circle.owner = self.request.user + circle.save() + + return HttpResponseRedirect(self.get_success_url()) + + def get_success_url(self): + return reverse("circles:circle_list") diff --git a/flangr/posts/migrations/0004_remove_post_public_post_shared_with.py b/flangr/posts/migrations/0004_remove_post_public_post_shared_with.py new file mode 100644 index 0000000..e3100e5 --- /dev/null +++ b/flangr/posts/migrations/0004_remove_post_public_post_shared_with.py @@ -0,0 +1,24 @@ +# Generated by Django 4.0.5 on 2022-07-09 13:27 + +from django.db import migrations +from django.db import models + + +class Migration(migrations.Migration): + + dependencies = [ + ("circles", "0001_initial"), + ("posts", "0003_alter_post_body_alter_post_title"), + ] + + operations = [ + migrations.RemoveField( + model_name="post", + name="public", + ), + migrations.AddField( + model_name="post", + name="shared_with", + field=models.ManyToManyField(to="circles.circle"), + ), + ] diff --git a/flangr/posts/migrations/0005_rename_shared_with_post_circles.py b/flangr/posts/migrations/0005_rename_shared_with_post_circles.py new file mode 100644 index 0000000..5546f25 --- /dev/null +++ b/flangr/posts/migrations/0005_rename_shared_with_post_circles.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.5 on 2022-07-09 13:28 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("posts", "0004_remove_post_public_post_shared_with"), + ] + + operations = [ + migrations.RenameField( + model_name="post", + old_name="shared_with", + new_name="circles", + ), + ] diff --git a/flangr/posts/models.py b/flangr/posts/models.py index ad1786e..1543fa6 100644 --- a/flangr/posts/models.py +++ b/flangr/posts/models.py @@ -1,8 +1,7 @@ import uuid -from django.db import models - from django.conf import settings +from django.db import models # Create your models here. @@ -15,13 +14,14 @@ def get_img_location(instance, filename): class Post(models.Model): img = models.ImageField(upload_to=get_img_location) posted = models.DateTimeField(auto_now_add=True) - public = models.BooleanField(default=False) title = models.CharField(max_length=255, blank=True, default="") body = models.TextField(blank=True, default="") user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) + circles = models.ManyToManyField("circles.Circle") + def __str__(self): return f"Post: {self.title} at {self.posted.strftime('%Y-%m-%d %H:%S')}" diff --git a/flangr/posts/views.py b/flangr/posts/views.py index c571a5d..f3685e3 100644 --- a/flangr/posts/views.py +++ b/flangr/posts/views.py @@ -1,15 +1,16 @@ -from django.views.generic import DetailView, CreateView -from django.views.generic.edit import ModelFormMixin +from django.contrib.auth.mixins import LoginRequiredMixin from django.forms import modelform_factory from django.http import HttpResponseRedirect from django.urls import reverse +from django.views.generic import CreateView +from django.views.generic import DetailView +from django.views.generic.edit import ModelFormMixin -from django.contrib.auth.mixins import LoginRequiredMixin +from .models import Comment +from .models import Post # Create your views here. -from .models import Post, Comment - class PostDetailView(LoginRequiredMixin, DetailView, ModelFormMixin): model = Post @@ -21,6 +22,7 @@ class PostDetailView(LoginRequiredMixin, DetailView, ModelFormMixin): comment.post = self.object comment.user = self.request.user comment.save() + return HttpResponseRedirect(self.get_success_url()) def get_queryset(self): @@ -37,7 +39,7 @@ class PostDetailView(LoginRequiredMixin, DetailView, ModelFormMixin): class PostCreateView(LoginRequiredMixin, CreateView): model = Post - fields = ("title", "body", "img") + fields = ("title", "body", "img", "circles") def get_success_url(self): return reverse("posts:post_detail", kwargs={"pk": self.object.pk}) @@ -46,4 +48,5 @@ class PostCreateView(LoginRequiredMixin, CreateView): self.object = form.save(commit=False) self.object.user = self.request.user self.object.save() + return HttpResponseRedirect(self.get_success_url()) diff --git a/flangr/templates/circles/circle_form.html b/flangr/templates/circles/circle_form.html new file mode 100644 index 0000000..1ccb008 --- /dev/null +++ b/flangr/templates/circles/circle_form.html @@ -0,0 +1,9 @@ +{% extends "base.html" %} +{% block content %} +

Circle {{ object.name }}

+
+ {% csrf_token %} + {{ form }} + +
+{% endblock content %} diff --git a/flangr/templates/circles/circle_list.html b/flangr/templates/circles/circle_list.html new file mode 100644 index 0000000..e608e86 --- /dev/null +++ b/flangr/templates/circles/circle_list.html @@ -0,0 +1,9 @@ +{% extends "base.html" %} +{% block content %} +

Your Circles

+ +{% endblock content %} diff --git a/flangr/templates/posts/post_form.html b/flangr/templates/posts/post_form.html index cac2d5e..66bfaba 100644 --- a/flangr/templates/posts/post_form.html +++ b/flangr/templates/posts/post_form.html @@ -5,6 +5,6 @@
{% csrf_token %} {{ form }} - +
{% endblock content %} diff --git a/flangr/urls.py b/flangr/urls.py index 805d746..19577ed 100644 --- a/flangr/urls.py +++ b/flangr/urls.py @@ -13,16 +13,17 @@ Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ -from django.contrib import admin -from django.urls import path, include - from django.conf import settings from django.conf.urls.static import static +from django.contrib import admin +from django.urls import include +from django.urls import path urlpatterns = [ path("admin/", admin.site.urls), path("accounts/", include("django.contrib.auth.urls")), path("posts/", include("flangr.posts.urls")), + path("circles/", include("flangr.circles.urls")), ] if settings.DEBUG: