From 7e63978aeb9fc2f92116925437279b37e409594d Mon Sep 17 00:00:00 2001
From: Monstar <31903668+Monstarrrr@users.noreply.github.com>
Date: Sun, 23 Jun 2024 04:38:43 +0200
Subject: [PATCH 01/18] Added post-new-argument form; Added API base for
posting new argument (untested)
---
frontend/app/(routes)/(auth)/login/page.tsx | 15 ++-
.../app/(routes)/(auth)/register/page.tsx | 15 ++-
frontend/app/_api/auth/login.ts | 2 +-
frontend/app/_api/posts/index.ts | 1 +
frontend/app/_api/posts/newPost.ts | 14 +++
.../app/_components/clientInitializer.tsx | 1 -
frontend/app/_components/form.tsx | 33 +++++--
frontend/app/_components/header.tsx | 4 +-
frontend/app/_types/FormProps.ts | 2 +-
frontend/app/_types/TextInput.ts | 2 +-
frontend/app/page.tsx | 93 ++++++++++++++++---
11 files changed, 151 insertions(+), 31 deletions(-)
create mode 100644 frontend/app/_api/posts/index.ts
create mode 100644 frontend/app/_api/posts/newPost.ts
diff --git a/frontend/app/(routes)/(auth)/login/page.tsx b/frontend/app/(routes)/(auth)/login/page.tsx
index ccf1e45..4f2a7c0 100644
--- a/frontend/app/(routes)/(auth)/login/page.tsx
+++ b/frontend/app/(routes)/(auth)/login/page.tsx
@@ -1,10 +1,10 @@
'use client'
-import { FormEvent, useState } from 'react'
+import { FormEvent, useEffect, useState } from 'react'
import { Form } from '@/components'
import { ApiResponse, TextInput } from '@/types'
import { formDataToObj } from '@/helpers'
-import { useAppDispatch } from '@/store/hooks'
+import { useAppDispatch, useAppSelector } from '@/store/hooks'
import { updateUser } from '@/store/slices/user'
import { useRouter } from 'next/navigation'
import { login, fetchUserInfo } from '@/api/auth'
@@ -22,6 +22,7 @@ const loginInputs: TextInput[] = [
value: '',
},
]
+const submitButtonLabel = 'Login'
const successMessage = 'Logged in successfully.'
export default function Login() {
@@ -31,6 +32,14 @@ export default function Login() {
const dispatch = useAppDispatch()
const router = useRouter()
+ const user = useAppSelector((state) => state.user.username)
+
+ useEffect(() => {
+ if (user) {
+ router.push('/')
+ }
+ }, [user, router])
+
async function handleSubmit(event: FormEvent) {
event.preventDefault()
setLoading(true)
@@ -61,7 +70,7 @@ export default function Login() {
onSubmit={handleSubmit}
loading={loading}
successMessage={success ? successMessage : null}
- buttonLabel='Login'
+ submitButtonLabel={submitButtonLabel}
/>
>
)
diff --git a/frontend/app/(routes)/(auth)/register/page.tsx b/frontend/app/(routes)/(auth)/register/page.tsx
index b707290..cfd9ded 100644
--- a/frontend/app/(routes)/(auth)/register/page.tsx
+++ b/frontend/app/(routes)/(auth)/register/page.tsx
@@ -1,15 +1,19 @@
'use client'
-import { FormEvent, useState } from 'react'
+import { FormEvent, useEffect, useState } from 'react'
import { Form } from '@/components'
import { ApiResponse, TextInput } from '@/types'
import { formDataToObj } from '@/helpers'
import { register } from '@/api/auth/register'
+import { useAppSelector } from '@/store/hooks'
+import { useRouter } from 'next/navigation'
export default function Register() {
const [isLoading, setIsLoading] = useState(false)
const [apiFormErrors, setApiFormErrors] = useState(null)
const [formSuccess, setFormSuccess] = useState(false)
+ const user = useAppSelector((state) => state.user.username)
+ const router = useRouter()
const registerInputs: TextInput[] = [
{
@@ -30,8 +34,15 @@ export default function Register() {
value: '',
},
]
+ const submitButtonLabel = 'Register'
const successMessage = 'Check your email to verify your account.'
+ useEffect(() => {
+ if (user) {
+ router.push('/')
+ }
+ }, [user, router])
+
async function handleSubmit(event: FormEvent) {
event.preventDefault()
setIsLoading(true)
@@ -56,7 +67,7 @@ export default function Register() {
Register
)
From f65b6080c7b08442cf25ea8955efc9039b4f19f1 Mon Sep 17 00:00:00 2001
From: Monstar <31903668+Monstarrrr@users.noreply.github.com>
Date: Sun, 23 Jun 2024 21:18:37 +0200
Subject: [PATCH 03/18] Formatting data
---
frontend/app/_types/FormDataObj.ts | 3 ++-
frontend/app/page.tsx | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/frontend/app/_types/FormDataObj.ts b/frontend/app/_types/FormDataObj.ts
index 918ee8b..ec7c804 100644
--- a/frontend/app/_types/FormDataObj.ts
+++ b/frontend/app/_types/FormDataObj.ts
@@ -1,3 +1,4 @@
export type FormDataObj = {
- [key: string]: string | File
+ // [key: string]: string | File
+ [key: string]: any
}
diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx
index 0c88a49..587a827 100644
--- a/frontend/app/page.tsx
+++ b/frontend/app/page.tsx
@@ -1,5 +1,5 @@
'use client'
-import { useAppDispatch, useAppSelector } from '@/store/hooks'
+import { useAppSelector } from '@/store/hooks'
import Link from 'next/link'
import { Form } from '@/components'
import type { TextInput } from '@/types'
From b05ff8cc8611ae1b4ddcc50b82453c6bd88b7dee Mon Sep 17 00:00:00 2001
From: Monstar <31903668+Monstarrrr@users.noreply.github.com>
Date: Tue, 25 Jun 2024 15:37:51 +0200
Subject: [PATCH 04/18] Add arguments & fetch them
---
..._remove_posts_acceptedanswerid_and_more.py | 97 +++++++++++++++++++
backend/core/models.py | 58 ++++++-----
backend/core/serializers.py | 5 +
.../_api/posts/{newPost.ts => createPost.ts} | 3 +-
frontend/app/_api/posts/getPosts.ts | 11 +++
frontend/app/_api/posts/index.ts | 3 +-
frontend/app/_components/argumentCard.tsx | 36 +++++++
frontend/app/_components/index.ts | 1 +
frontend/app/_types/Post.ts | 6 ++
frontend/app/_types/index.ts | 1 +
frontend/app/page.tsx | 12 ++-
11 files changed, 205 insertions(+), 28 deletions(-)
create mode 100644 backend/core/migrations/0003_remove_posts_acceptedanswerid_and_more.py
rename frontend/app/_api/posts/{newPost.ts => createPost.ts} (76%)
create mode 100644 frontend/app/_api/posts/getPosts.ts
create mode 100644 frontend/app/_components/argumentCard.tsx
create mode 100644 frontend/app/_types/Post.ts
diff --git a/backend/core/migrations/0003_remove_posts_acceptedanswerid_and_more.py b/backend/core/migrations/0003_remove_posts_acceptedanswerid_and_more.py
new file mode 100644
index 0000000..e189cdc
--- /dev/null
+++ b/backend/core/migrations/0003_remove_posts_acceptedanswerid_and_more.py
@@ -0,0 +1,97 @@
+# Generated by Django 5.0.4 on 2024-06-24 17:31
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("core", "0002_alter_posts_answercount_alter_posts_commentcount_and_more"),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name="posts",
+ name="acceptedAnswerId",
+ ),
+ migrations.RemoveField(
+ model_name="posts",
+ name="answerCount",
+ ),
+ migrations.RemoveField(
+ model_name="posts",
+ name="closedDate",
+ ),
+ migrations.RemoveField(
+ model_name="posts",
+ name="commentCount",
+ ),
+ migrations.RemoveField(
+ model_name="posts",
+ name="communityOwnedDate",
+ ),
+ migrations.RemoveField(
+ model_name="posts",
+ name="creationDate",
+ ),
+ migrations.RemoveField(
+ model_name="posts",
+ name="deletionDate",
+ ),
+ migrations.RemoveField(
+ model_name="posts",
+ name="favoriteCount",
+ ),
+ migrations.RemoveField(
+ model_name="posts",
+ name="lastActivityDate",
+ ),
+ migrations.RemoveField(
+ model_name="posts",
+ name="lastEditDate",
+ ),
+ migrations.RemoveField(
+ model_name="posts",
+ name="lastEditorDisplayName",
+ ),
+ migrations.RemoveField(
+ model_name="posts",
+ name="lastEditorUserId",
+ ),
+ migrations.RemoveField(
+ model_name="posts",
+ name="ownerDisplayName",
+ ),
+ migrations.RemoveField(
+ model_name="posts",
+ name="parentId",
+ ),
+ migrations.RemoveField(
+ model_name="posts",
+ name="postTypeId",
+ ),
+ migrations.RemoveField(
+ model_name="posts",
+ name="score",
+ ),
+ migrations.RemoveField(
+ model_name="posts",
+ name="tags",
+ ),
+ migrations.RemoveField(
+ model_name="posts",
+ name="viewcount",
+ ),
+ migrations.AddField(
+ model_name="posts",
+ name="type",
+ field=models.CharField(
+ choices=[
+ ("argument", "argument"),
+ ("rebuttal", "rebuttal"),
+ ("comment", "comment"),
+ ],
+ default="argument",
+ max_length=10,
+ ),
+ ),
+ ]
diff --git a/backend/core/models.py b/backend/core/models.py
index 63b418e..8ae0fbd 100644
--- a/backend/core/models.py
+++ b/backend/core/models.py
@@ -8,6 +8,14 @@
USERNAME_MAX_LEN = 255
AVATAR_MAX_LEN = 255
BIO_MAX_LEN = 255
+ARGUMENT = "argument"
+REBUTTAL = "rebuttal"
+COMMENT = "comment"
+POSTS_TYPES = [
+ (ARGUMENT, "argument"),
+ (REBUTTAL, "rebuttal"),
+ (COMMENT, "comment"),
+]
class Tags(models.Model):
@@ -31,31 +39,35 @@ class Posts(models.Model):
Tag wikis (4)
"""
- postTypeId: models.IntegerField = models.IntegerField()
- acceptedAnswerId: models.IntegerField = models.IntegerField(null=True)
- parentId: models.IntegerField = models.IntegerField(null=True)
- creationDate: models.DateField = models.DateField()
- deletionDate: models.DateField = models.DateField(null=True)
- score: models.IntegerField = models.IntegerField(default=0)
- viewcount: models.IntegerField = models.IntegerField(default=0, null=True)
- body: models.TextField = models.TextField() # render as HTML
- ownerUserId: models.IntegerField = models.IntegerField(null=True)
- ownerDisplayName: models.CharField = models.CharField(
- max_length=OWNER_MAX_LEN, null=True
- )
- lastEditorUserId: models.IntegerField = models.IntegerField(null=True)
- lastEditorDisplayName: models.CharField = models.CharField(
- max_length=LAST_EDITOR_MAX_LEN, null=True
+ type: models.CharField = models.CharField(
+ max_length=10, choices=POSTS_TYPES, default=ARGUMENT
)
- lastEditDate: models.DateField = models.DateField(null=True)
- lastActivityDate: models.DateField = models.DateField()
+ body: models.TextField = models.TextField() # render as HTML
title: models.CharField = models.CharField(max_length=TITLE_MAX_LEN)
- tags: models.ManyToManyField = models.ManyToManyField(Tags, related_name="tags")
- answerCount: models.IntegerField = models.IntegerField(default=0, null=True)
- commentCount: models.IntegerField = models.IntegerField(default=0, null=True)
- favoriteCount: models.IntegerField = models.IntegerField(default=0, null=True)
- closedDate: models.DateField = models.DateField(null=True)
- communityOwnedDate: models.DateField = models.DateField(null=True)
+ ownerUserId: models.IntegerField = models.IntegerField(null=True)
+ # MVP moment:
+ # acceptedAnswerId: models.IntegerField = models.IntegerField(null=True)
+ # parentId: models.IntegerField = models.IntegerField(null=True)
+ # created_at: models.DateTimeField = models.DateTimeField(auto_now_add=True)
+ # updated_at: models.DateTimeField = models.DateTimeField(auto_now=True)
+ # deleted_at: models.DateTimeField = models.DateTimeField(null=True)
+ # score: models.IntegerField = models.IntegerField(default=0)
+ # viewcount: models.IntegerField = models.IntegerField(default=0, null=True)
+ # ownerDisplayName: models.CharField = models.CharField(
+ # max_length=OWNER_MAX_LEN, null=True
+ # )
+ # lastEditorUserId: models.IntegerField = models.IntegerField(null=True)
+ # lastEditorDisplayName: models.CharField = models.CharField(
+ # max_length=LAST_EDITOR_MAX_LEN, null=True
+ # )
+ # lastEditDate: models.DateField = models.DateField(null=True)
+ # lastActivityDate: models.DateField = models.DateField()
+ # tags: models.ManyToManyField = models.ManyToManyField(Tags, related_name="tags")
+ # answerCount: models.IntegerField = models.IntegerField(default=0, null=True)
+ # commentCount: models.IntegerField = models.IntegerField(default=0, null=True)
+ # favoriteCount: models.IntegerField = models.IntegerField(default=0, null=True)
+ # closedDate: models.DateField = models.DateField(null=True)
+ # communityOwnedDate: models.DateField = models.DateField(null=True)
class UserProfile(models.Model):
diff --git a/backend/core/serializers.py b/backend/core/serializers.py
index a6937d7..69507e4 100644
--- a/backend/core/serializers.py
+++ b/backend/core/serializers.py
@@ -14,6 +14,11 @@ class Meta:
model = Posts
fields = "__all__"
+ def validate_type(self, value):
+ if value not in ["argument", "rebuttal", "comment"]:
+ raise serializers.ValidationError("Invalid post type")
+ return value
+
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
diff --git a/frontend/app/_api/posts/newPost.ts b/frontend/app/_api/posts/createPost.ts
similarity index 76%
rename from frontend/app/_api/posts/newPost.ts
rename to frontend/app/_api/posts/createPost.ts
index d089afd..7080bfc 100644
--- a/frontend/app/_api/posts/newPost.ts
+++ b/frontend/app/_api/posts/createPost.ts
@@ -1,10 +1,11 @@
import { FormDataObj } from '@/types'
import api from '@/api/api'
-export const newPost = async (formData: FormDataObj) => {
+export const createPost = async (formData: FormDataObj) => {
try {
const response = await api.post('api/posts', {
...formData,
+ type: 'argument',
})
return response.data
} catch (error) {
diff --git a/frontend/app/_api/posts/getPosts.ts b/frontend/app/_api/posts/getPosts.ts
new file mode 100644
index 0000000..51d02f6
--- /dev/null
+++ b/frontend/app/_api/posts/getPosts.ts
@@ -0,0 +1,11 @@
+import api from '@/api/api'
+
+export const getPosts = async () => {
+ try {
+ const response = await api.get('api/posts')
+ return response.data
+ } catch (error) {
+ console.error('# "Get posts" request failed: ', error)
+ throw error
+ }
+}
diff --git a/frontend/app/_api/posts/index.ts b/frontend/app/_api/posts/index.ts
index afe74c2..59a728b 100644
--- a/frontend/app/_api/posts/index.ts
+++ b/frontend/app/_api/posts/index.ts
@@ -1 +1,2 @@
-export * from '@/api/posts/newPost'
+export * from '@/api/posts/createPost'
+export * from '@/api/posts/getPosts'
diff --git a/frontend/app/_components/argumentCard.tsx b/frontend/app/_components/argumentCard.tsx
new file mode 100644
index 0000000..988f301
--- /dev/null
+++ b/frontend/app/_components/argumentCard.tsx
@@ -0,0 +1,36 @@
+'use client'
+import { Post } from '@/types'
+import { getPosts } from '@/api/posts'
+import { useEffect, useState } from 'react'
+import Link from 'next/link'
+
+export default function ArgumentCard() {
+ const [posts, setPosts] = useState([])
+
+ useEffect(() => {
+ let fetchApi = async () => {
+ try {
+ const response = await getPosts()
+ setPosts(response)
+ } catch (error) {
+ console.error('# Error fetching posts: ', error)
+ }
+ }
+ fetchApi()
+ }, [])
+
+ return (
+
+ {posts.map((post) => (
+
+
+
+ {post.title}
+
+
{post.body}
+
+
+ ))}
+
+ )
+}
diff --git a/frontend/app/_components/index.ts b/frontend/app/_components/index.ts
index 23cd7bd..a444e19 100644
--- a/frontend/app/_components/index.ts
+++ b/frontend/app/_components/index.ts
@@ -2,3 +2,4 @@ export { default as Header } from '@/components/header'
export { default as Form } from '@/components/form'
export { default as Activate } from '@/components/activate'
export { default as ClientInitializer } from '@/components/clientInitializer'
+export { default as ArgumentCard } from '@/components/argumentCard'
diff --git a/frontend/app/_types/Post.ts b/frontend/app/_types/Post.ts
new file mode 100644
index 0000000..cd4b64b
--- /dev/null
+++ b/frontend/app/_types/Post.ts
@@ -0,0 +1,6 @@
+export type Post = {
+ id: number
+ title: string
+ body: string
+ type: string
+}
diff --git a/frontend/app/_types/index.ts b/frontend/app/_types/index.ts
index 74c33f9..cdd6c7b 100644
--- a/frontend/app/_types/index.ts
+++ b/frontend/app/_types/index.ts
@@ -4,3 +4,4 @@ export type { FormDataObj } from '@/types/FormDataObj'
export type { UserInfo } from '@/types/UserType'
export type { FormProps } from '@/types/FormProps'
export type { Token } from '@/types/Token'
+export type { Post } from '@/types/Post'
diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx
index 587a827..dbee56e 100644
--- a/frontend/app/page.tsx
+++ b/frontend/app/page.tsx
@@ -1,10 +1,10 @@
'use client'
import { useAppSelector } from '@/store/hooks'
import Link from 'next/link'
-import { Form } from '@/components'
+import { Form, ArgumentCard } from '@/components'
import type { TextInput } from '@/types'
import { FormEvent, useState } from 'react'
-import { newPost } from '@/api/posts'
+import { createPost } from '@/api/posts'
import { formDataToObj } from '@/helpers'
// postTypeId: 1,
@@ -54,7 +54,7 @@ export default function Home() {
const formData = formDataToObj(event)
try {
- await newPost(formData)
+ await createPost({ ...formData, postTypeId: 1 })
setLoading(false)
setSuccess(true)
} catch (error: any) {
@@ -88,6 +88,12 @@ export default function Home() {
Register to start sharing your rebuttals!
)}
+
+
+
+ All arguments
+
+
>
)
}
From 1da642f81e1c2d2036ff3cf254253cdd537a91f3 Mon Sep 17 00:00:00 2001
From: Monstar <31903668+Monstarrrr@users.noreply.github.com>
Date: Tue, 25 Jun 2024 15:47:08 +0200
Subject: [PATCH 05/18] Fix types
---
backend/core/models.py | 3 +++
backend/core/serializers.py | 10 +++++++++-
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/backend/core/models.py b/backend/core/models.py
index 8ae0fbd..af64e73 100644
--- a/backend/core/models.py
+++ b/backend/core/models.py
@@ -69,6 +69,9 @@ class Posts(models.Model):
# closedDate: models.DateField = models.DateField(null=True)
# communityOwnedDate: models.DateField = models.DateField(null=True)
+ def __str__(self):
+ return f"{self.title} ({self.type})"
+
class UserProfile(models.Model):
# Public
diff --git a/backend/core/serializers.py b/backend/core/serializers.py
index 69507e4..1e10170 100644
--- a/backend/core/serializers.py
+++ b/backend/core/serializers.py
@@ -12,7 +12,15 @@ class Meta:
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Posts
- fields = "__all__"
+ fields = [
+ "id",
+ "type",
+ "body",
+ "title",
+ "ownerUserId",
+ "created_at",
+ "updated_at",
+ ]
def validate_type(self, value):
if value not in ["argument", "rebuttal", "comment"]:
From 3046c21514f89ad3e88c36bf7597f55ea4922c8f Mon Sep 17 00:00:00 2001
From: Monstar <31903668+Monstarrrr@users.noreply.github.com>
Date: Tue, 25 Jun 2024 15:52:57 +0200
Subject: [PATCH 06/18] Edited post tests
---
backend/core/models.py | 4 ++--
backend/core/tests/test_posts.py | 35 ++++++++++++++++----------------
2 files changed, 20 insertions(+), 19 deletions(-)
diff --git a/backend/core/models.py b/backend/core/models.py
index af64e73..c678b82 100644
--- a/backend/core/models.py
+++ b/backend/core/models.py
@@ -45,11 +45,11 @@ class Posts(models.Model):
body: models.TextField = models.TextField() # render as HTML
title: models.CharField = models.CharField(max_length=TITLE_MAX_LEN)
ownerUserId: models.IntegerField = models.IntegerField(null=True)
+ created_at: models.DateTimeField = models.DateTimeField(auto_now_add=True)
+ updated_at: models.DateTimeField = models.DateTimeField(auto_now=True)
# MVP moment:
# acceptedAnswerId: models.IntegerField = models.IntegerField(null=True)
# parentId: models.IntegerField = models.IntegerField(null=True)
- # created_at: models.DateTimeField = models.DateTimeField(auto_now_add=True)
- # updated_at: models.DateTimeField = models.DateTimeField(auto_now=True)
# deleted_at: models.DateTimeField = models.DateTimeField(null=True)
# score: models.IntegerField = models.IntegerField(default=0)
# viewcount: models.IntegerField = models.IntegerField(default=0, null=True)
diff --git a/backend/core/tests/test_posts.py b/backend/core/tests/test_posts.py
index 87ecc02..740689f 100644
--- a/backend/core/tests/test_posts.py
+++ b/backend/core/tests/test_posts.py
@@ -23,26 +23,27 @@ def setUp(self):
# Create sample post
self.sample_post = Posts.objects.create(
- postTypeId=1,
- acceptedAnswerId=1,
- parentId=1,
- creationDate="2024-01-01",
- deletionDate=None,
- score=10,
- viewcount=100,
+ type="argument",
+ # acceptedAnswerId=1,
+ # parentId=1,
+ created_at="2024-01-01",
+ edited_at="2024-01-02",
+ # deletedAt=None,
+ # score=10,
+ # viewcount=100,
body="Sample post content
",
ownerUserId=1,
- ownerDisplayName="John Doe",
- lastEditorUserId=1,
- lastEditorDisplayName="Jane Doe",
- lastEditDate="2024-01-02",
- lastActivityDate="2024-01-03",
+ # ownerDisplayName="John Doe",
+ # lastEditorUserId=1,
+ # lastEditorDisplayName="Jane Doe",
+ # lastEditDate="2024-01-02",
+ # lastActivityDate="2024-01-03",
title="Sample Title",
- answerCount=0,
- commentCount=0,
- favoriteCount=0,
- closedDate=None,
- communityOwnedDate=None,
+ # answerCount=0,
+ # commentCount=0,
+ # favoriteCount=0,
+ # closedDate=None,
+ # communityOwnedDate=None,
)
# Correctly assign the tag to the post
self.sample_post.tags.set([self.sample_tag])
From 916c103bbe6eca48d9175409a509f96b3d243906 Mon Sep 17 00:00:00 2001
From: Ziggy
Date: Tue, 25 Jun 2024 17:29:09 +0200
Subject: [PATCH 07/18] remove deprecated comments and use camelCase
---
.../0004_posts_createdat_posts_updatedat.py | 26 ++++++++++++++
backend/core/models.py | 36 ++-----------------
2 files changed, 29 insertions(+), 33 deletions(-)
create mode 100644 backend/core/migrations/0004_posts_createdat_posts_updatedat.py
diff --git a/backend/core/migrations/0004_posts_createdat_posts_updatedat.py b/backend/core/migrations/0004_posts_createdat_posts_updatedat.py
new file mode 100644
index 0000000..26aff44
--- /dev/null
+++ b/backend/core/migrations/0004_posts_createdat_posts_updatedat.py
@@ -0,0 +1,26 @@
+# Generated by Django 5.0.4 on 2024-06-25 14:53
+
+import django.utils.timezone
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("core", "0003_remove_posts_acceptedanswerid_and_more"),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="posts",
+ name="createdAt",
+ field=models.DateTimeField(
+ auto_now_add=True, default=django.utils.timezone.now
+ ),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name="posts",
+ name="updatedAt",
+ field=models.DateTimeField(auto_now=True),
+ ),
+ ]
diff --git a/backend/core/models.py b/backend/core/models.py
index c678b82..6384865 100644
--- a/backend/core/models.py
+++ b/backend/core/models.py
@@ -28,16 +28,7 @@ class Tags(models.Model):
class Posts(models.Model):
- """Posts database model
-
- Posts include the following types, marked
- by their postTypeId
-
- Arguments (1)
- Rebuttals (2)
- Comments (3)
- Tag wikis (4)
- """
+ """Posts database model"""
type: models.CharField = models.CharField(
max_length=10, choices=POSTS_TYPES, default=ARGUMENT
@@ -45,29 +36,8 @@ class Posts(models.Model):
body: models.TextField = models.TextField() # render as HTML
title: models.CharField = models.CharField(max_length=TITLE_MAX_LEN)
ownerUserId: models.IntegerField = models.IntegerField(null=True)
- created_at: models.DateTimeField = models.DateTimeField(auto_now_add=True)
- updated_at: models.DateTimeField = models.DateTimeField(auto_now=True)
- # MVP moment:
- # acceptedAnswerId: models.IntegerField = models.IntegerField(null=True)
- # parentId: models.IntegerField = models.IntegerField(null=True)
- # deleted_at: models.DateTimeField = models.DateTimeField(null=True)
- # score: models.IntegerField = models.IntegerField(default=0)
- # viewcount: models.IntegerField = models.IntegerField(default=0, null=True)
- # ownerDisplayName: models.CharField = models.CharField(
- # max_length=OWNER_MAX_LEN, null=True
- # )
- # lastEditorUserId: models.IntegerField = models.IntegerField(null=True)
- # lastEditorDisplayName: models.CharField = models.CharField(
- # max_length=LAST_EDITOR_MAX_LEN, null=True
- # )
- # lastEditDate: models.DateField = models.DateField(null=True)
- # lastActivityDate: models.DateField = models.DateField()
- # tags: models.ManyToManyField = models.ManyToManyField(Tags, related_name="tags")
- # answerCount: models.IntegerField = models.IntegerField(default=0, null=True)
- # commentCount: models.IntegerField = models.IntegerField(default=0, null=True)
- # favoriteCount: models.IntegerField = models.IntegerField(default=0, null=True)
- # closedDate: models.DateField = models.DateField(null=True)
- # communityOwnedDate: models.DateField = models.DateField(null=True)
+ createdAt: models.DateTimeField = models.DateTimeField(auto_now_add=True)
+ updatedAt: models.DateTimeField = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.title} ({self.type})"
From 31ec470e1723450bd9f3286dd1bd2103019258f7 Mon Sep 17 00:00:00 2001
From: Ziggy
Date: Tue, 25 Jun 2024 17:32:29 +0200
Subject: [PATCH 08/18] add conventions to CONTRIBUTING.md
---
docs/CONTRIBUTING.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md
index 2d1f633..d180573 100644
--- a/docs/CONTRIBUTING.md
+++ b/docs/CONTRIBUTING.md
@@ -16,6 +16,7 @@ Please take a moment to review this document to understand how you can contribut
- [1.3. Verify install](#13-verify-install)
- [1.4. Skip pre-commit](#14-skip-pre-commit)
- [2. Running Django](#2-running-django)
+ - [3. Conventions](#3-conventions)
## Ways to Contribute 🚀
@@ -95,3 +96,8 @@ Access the OpenAPI spec in three ways while running the django server:
- Use Swagger UI at `/api/schema/swagger-ui/`
- Use Redoc UI at `/api/schema/redoc-ui/`
+### 3. Conventions
+
+#### 3.1 Database table and column names
+
+We use camelCase for our database tables and column names defined in `models.py`.
From 2579448c4c2500b461d8a9651ea5dddb2ade09c6 Mon Sep 17 00:00:00 2001
From: Ziggy
Date: Tue, 25 Jun 2024 17:53:27 +0200
Subject: [PATCH 09/18] validate against post types from models.py
---
backend/core/models.py | 3 ---
backend/core/serializers.py | 16 +++++-----------
2 files changed, 5 insertions(+), 14 deletions(-)
diff --git a/backend/core/models.py b/backend/core/models.py
index 6384865..c979432 100644
--- a/backend/core/models.py
+++ b/backend/core/models.py
@@ -39,9 +39,6 @@ class Posts(models.Model):
createdAt: models.DateTimeField = models.DateTimeField(auto_now_add=True)
updatedAt: models.DateTimeField = models.DateTimeField(auto_now=True)
- def __str__(self):
- return f"{self.title} ({self.type})"
-
class UserProfile(models.Model):
# Public
diff --git a/backend/core/serializers.py b/backend/core/serializers.py
index 1e10170..017e8bb 100644
--- a/backend/core/serializers.py
+++ b/backend/core/serializers.py
@@ -1,6 +1,6 @@
from rest_framework import serializers
-from .models import Posts, Tags, UserProfile
+from .models import POSTS_TYPES, Posts, Tags, UserProfile
class TagSerializer(serializers.ModelSerializer):
@@ -12,18 +12,12 @@ class Meta:
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Posts
- fields = [
- "id",
- "type",
- "body",
- "title",
- "ownerUserId",
- "created_at",
- "updated_at",
- ]
+ fields = "__all__"
def validate_type(self, value):
- if value not in ["argument", "rebuttal", "comment"]:
+ if (
+ value not in list(zip(*POSTS_TYPES))[0]
+ ): # validate against post types in models.py
raise serializers.ValidationError("Invalid post type")
return value
From 9f6c1fc65c300048c0ccac857f1d6088f949f166 Mon Sep 17 00:00:00 2001
From: Ziggy
Date: Tue, 25 Jun 2024 17:58:11 +0200
Subject: [PATCH 10/18] remove redundant verifier
---
backend/core/serializers.py | 9 +--------
1 file changed, 1 insertion(+), 8 deletions(-)
diff --git a/backend/core/serializers.py b/backend/core/serializers.py
index 017e8bb..a6937d7 100644
--- a/backend/core/serializers.py
+++ b/backend/core/serializers.py
@@ -1,6 +1,6 @@
from rest_framework import serializers
-from .models import POSTS_TYPES, Posts, Tags, UserProfile
+from .models import Posts, Tags, UserProfile
class TagSerializer(serializers.ModelSerializer):
@@ -14,13 +14,6 @@ class Meta:
model = Posts
fields = "__all__"
- def validate_type(self, value):
- if (
- value not in list(zip(*POSTS_TYPES))[0]
- ): # validate against post types in models.py
- raise serializers.ValidationError("Invalid post type")
- return value
-
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
From 30ca551cd0a52fc14d57c7ebc1bf7267da620892 Mon Sep 17 00:00:00 2001
From: Ziggy
Date: Tue, 25 Jun 2024 21:04:33 +0200
Subject: [PATCH 11/18] added auth for PostViewSet
---
backend/core/views.py | 44 ++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 43 insertions(+), 1 deletion(-)
diff --git a/backend/core/views.py b/backend/core/views.py
index 0285f44..6b85c57 100644
--- a/backend/core/views.py
+++ b/backend/core/views.py
@@ -10,18 +10,50 @@
from django.utils.http import urlsafe_base64_encode
from djoser.views import UserViewSet
from rest_framework import viewsets
+from rest_framework.permissions import (
+ SAFE_METHODS,
+ BasePermission,
+ IsAdminUser,
+ IsAuthenticated,
+)
from rest_framework.response import Response
from .forms import UserRegisterForm
from .models import Posts, Tags, UserProfile
-from .serializers import PostSerializer, TagSerializer, UserProfileSerializer
+from .serializers import (
+ # ArgumentSerializer,
+ PostSerializer,
+ TagSerializer,
+ UserProfileSerializer,
+)
from .token import account_activation_token
+class IsOwnerOrReadOnly(BasePermission):
+ """
+ Object-level permission to only allow owners of an object to edit it.
+ Assumes the model instance has an `owner` attribute.
+ """
+
+ def has_object_permission(self, request, view, obj):
+ # Read permissions are allowed to any request,
+ # so we'll always allow GET, HEAD or OPTIONS requests.
+ if request.method in SAFE_METHODS:
+ return True
+
+ # Instance must have an attribute named `owner`.
+ return obj.owner == request.user
+
+
def success(request):
return HttpResponse("", status=200)
+"""class ArgumentViewSet(viewsets.ModelViewSet):
+ queryset = Posts.objects.get(type="argument")
+ serializer_class = ArgumentSerializer"""
+
+
class TagViewSet(viewsets.ModelViewSet):
queryset = Tags.objects.all()
serializer_class = TagSerializer
@@ -31,6 +63,16 @@ class PostViewSet(viewsets.ModelViewSet):
queryset = Posts.objects.all()
serializer_class = PostSerializer
+ def get_permissions(self):
+ if self.action == "create":
+ print("creating!")
+ return [IsAuthenticated()]
+ if self.action == "update" or self.action == "delete":
+ print("abc")
+ return [IsOwnerOrReadOnly(), IsAdminUser()]
+ print("what??")
+ return []
+
class UserProfileViewSet(viewsets.ModelViewSet):
queryset = UserProfile.objects.all()
From a7dec91bea1c46b6d74c81d1dea4d799ba375ca9 Mon Sep 17 00:00:00 2001
From: Ziggy
Date: Tue, 25 Jun 2024 21:07:05 +0200
Subject: [PATCH 12/18] remove print debugs
---
backend/core/views.py | 3 ---
1 file changed, 3 deletions(-)
diff --git a/backend/core/views.py b/backend/core/views.py
index 6b85c57..8aa2faa 100644
--- a/backend/core/views.py
+++ b/backend/core/views.py
@@ -65,12 +65,9 @@ class PostViewSet(viewsets.ModelViewSet):
def get_permissions(self):
if self.action == "create":
- print("creating!")
return [IsAuthenticated()]
if self.action == "update" or self.action == "delete":
- print("abc")
return [IsOwnerOrReadOnly(), IsAdminUser()]
- print("what??")
return []
From 4fd2527ba5da3946caeb50c245c4593854acb61c Mon Sep 17 00:00:00 2001
From: Ziggy
Date: Wed, 26 Jun 2024 04:19:10 +0200
Subject: [PATCH 13/18] add arguments viewset and posts permissions
---
backend/core/serializers.py | 9 +++++++++
backend/core/views.py | 28 ++++++++++++++++++++--------
backend/rebutify/urls.py | 2 +-
3 files changed, 30 insertions(+), 9 deletions(-)
diff --git a/backend/core/serializers.py b/backend/core/serializers.py
index a6937d7..d83c327 100644
--- a/backend/core/serializers.py
+++ b/backend/core/serializers.py
@@ -9,6 +9,15 @@ class Meta:
fields = "__all__"
+class ArgumentSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = Posts
+ fields = ["id", "body", "title", "ownerUserId", "createdAt", "updatedAt"]
+ read_only_fields = [
+ "ownerUserId",
+ ]
+
+
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Posts
diff --git a/backend/core/views.py b/backend/core/views.py
index 8aa2faa..250d14d 100644
--- a/backend/core/views.py
+++ b/backend/core/views.py
@@ -12,6 +12,7 @@
from rest_framework import viewsets
from rest_framework.permissions import (
SAFE_METHODS,
+ AllowAny,
BasePermission,
IsAdminUser,
IsAuthenticated,
@@ -21,7 +22,7 @@
from .forms import UserRegisterForm
from .models import Posts, Tags, UserProfile
from .serializers import (
- # ArgumentSerializer,
+ ArgumentSerializer,
PostSerializer,
TagSerializer,
UserProfileSerializer,
@@ -40,7 +41,6 @@ def has_object_permission(self, request, view, obj):
# so we'll always allow GET, HEAD or OPTIONS requests.
if request.method in SAFE_METHODS:
return True
-
# Instance must have an attribute named `owner`.
return obj.owner == request.user
@@ -49,9 +49,21 @@ def success(request):
return HttpResponse("", status=200)
-"""class ArgumentViewSet(viewsets.ModelViewSet):
- queryset = Posts.objects.get(type="argument")
- serializer_class = ArgumentSerializer"""
+class ArgumentViewSet(viewsets.ModelViewSet):
+ queryset = Posts.objects.filter(type="argument")
+ serializer_class = ArgumentSerializer
+
+ def perform_create(self, serializer):
+ serializer.save(ownerUserId=self.request.user.id)
+
+ def get_permissions(self):
+ if self.action == "create":
+ return [IsAuthenticated()]
+ if self.action == "update" or self.action == "delete":
+ return [
+ IsOwnerOrReadOnly() | IsAdminUser(),
+ ]
+ return [AllowAny()]
class TagViewSet(viewsets.ModelViewSet):
@@ -66,9 +78,9 @@ class PostViewSet(viewsets.ModelViewSet):
def get_permissions(self):
if self.action == "create":
return [IsAuthenticated()]
- if self.action == "update" or self.action == "delete":
- return [IsOwnerOrReadOnly(), IsAdminUser()]
- return []
+ if self.action in ["update", "delete", "partial_update"]:
+ return [IsOwnerOrReadOnly() | IsAdminUser()]
+ return [AllowAny()]
class UserProfileViewSet(viewsets.ModelViewSet):
diff --git a/backend/rebutify/urls.py b/backend/rebutify/urls.py
index 229a07b..85d62f1 100644
--- a/backend/rebutify/urls.py
+++ b/backend/rebutify/urls.py
@@ -31,9 +31,9 @@
router.register(r"status/alive", views.StatusViewSet, basename="alive")
router.register(r"tags", views.TagViewSet, basename="tags")
router.register(r"posts", views.PostViewSet, basename="posts")
+router.register(r"arguments", views.ArgumentViewSet, basename="arguments")
router.register(r"user-profile", views.UserProfileViewSet, basename="user-profile")
-
urlpatterns = [
path(os.getenv("DJANGO_ADMIN_PATH", "admin/"), admin.site.urls),
path("api/", include(router.urls)),
From 723c18c879ea8b2ae262124fec43cc6caf98d771 Mon Sep 17 00:00:00 2001
From: Ziggy
Date: Wed, 26 Jun 2024 04:19:35 +0200
Subject: [PATCH 14/18] disable basic and session auth
---
backend/rebutify/settings.py | 2 --
1 file changed, 2 deletions(-)
diff --git a/backend/rebutify/settings.py b/backend/rebutify/settings.py
index 772113f..f6184c4 100644
--- a/backend/rebutify/settings.py
+++ b/backend/rebutify/settings.py
@@ -90,8 +90,6 @@
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
- "rest_framework.authentication.BasicAuthentication",
- "rest_framework.authentication.SessionAuthentication",
"rest_framework_simplejwt.authentication.JWTAuthentication",
],
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
From 7ef24be1c088592b364a3d449da1fe4335dba75e Mon Sep 17 00:00:00 2001
From: Ziggy
Date: Wed, 26 Jun 2024 04:33:11 +0200
Subject: [PATCH 15/18] fix: broken/jumbled tests
---
backend/core/migrations/0005_delete_tags.py | 15 +++++
backend/core/models.py | 10 ---
backend/core/serializers.py | 8 +--
backend/core/tests/test_posts.py | 72 ++-------------------
backend/core/tests/test_user.py | 43 ++++++++++++
backend/core/views.py | 8 +--
backend/rebutify/urls.py | 1 -
7 files changed, 64 insertions(+), 93 deletions(-)
create mode 100644 backend/core/migrations/0005_delete_tags.py
create mode 100644 backend/core/tests/test_user.py
diff --git a/backend/core/migrations/0005_delete_tags.py b/backend/core/migrations/0005_delete_tags.py
new file mode 100644
index 0000000..33ec9a3
--- /dev/null
+++ b/backend/core/migrations/0005_delete_tags.py
@@ -0,0 +1,15 @@
+# Generated by Django 5.0.4 on 2024-06-26 02:24
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("core", "0004_posts_createdat_posts_updatedat"),
+ ]
+
+ operations = [
+ migrations.DeleteModel(
+ name="Tags",
+ ),
+ ]
diff --git a/backend/core/models.py b/backend/core/models.py
index c979432..2dc00d6 100644
--- a/backend/core/models.py
+++ b/backend/core/models.py
@@ -1,7 +1,6 @@
from django.contrib.auth.models import User
from django.db import models
-TAG_NAME_MAX_LEN = 50
TITLE_MAX_LEN = 255
LAST_EDITOR_MAX_LEN = 255
OWNER_MAX_LEN = 255
@@ -18,15 +17,6 @@
]
-class Tags(models.Model):
- tagName: models.CharField = models.CharField(max_length=TAG_NAME_MAX_LEN)
- count: models.IntegerField = models.IntegerField(default=0)
- excerptPostId: models.IntegerField = models.IntegerField(null=True)
- wikiPostId: models.IntegerField = models.IntegerField(null=True)
- isModeratorOnly: models.BooleanField = models.BooleanField(default=False)
- isRequired: models.BooleanField = models.BooleanField(default=False)
-
-
class Posts(models.Model):
"""Posts database model"""
diff --git a/backend/core/serializers.py b/backend/core/serializers.py
index d83c327..9ba3512 100644
--- a/backend/core/serializers.py
+++ b/backend/core/serializers.py
@@ -1,12 +1,6 @@
from rest_framework import serializers
-from .models import Posts, Tags, UserProfile
-
-
-class TagSerializer(serializers.ModelSerializer):
- class Meta:
- model = Tags
- fields = "__all__"
+from .models import Posts, UserProfile
class ArgumentSerializer(serializers.ModelSerializer):
diff --git a/backend/core/tests/test_posts.py b/backend/core/tests/test_posts.py
index 740689f..04d98f6 100644
--- a/backend/core/tests/test_posts.py
+++ b/backend/core/tests/test_posts.py
@@ -1,90 +1,26 @@
-# Create your tests here.
-from django.contrib.auth.models import User
from django.test import Client, TestCase
from django.urls import reverse
-from ..models import Posts, Tags, UserProfile
+from core.models import Posts
-class CoreTests(TestCase):
+class PostsTests(TestCase):
def setUp(self):
# Create a client instance
self.client = Client()
- # Create sample tag
- self.sample_tag = Tags.objects.create(
- tagName="tag1",
- count=1,
- excerptPostId=1,
- wikiPostId=1,
- isModeratorOnly=False,
- isRequired=True,
- )
-
# Create sample post
self.sample_post = Posts.objects.create(
type="argument",
- # acceptedAnswerId=1,
- # parentId=1,
- created_at="2024-01-01",
- edited_at="2024-01-02",
- # deletedAt=None,
- # score=10,
- # viewcount=100,
+ createdAt="2024-06-26 02:20:58.689998+00:00",
+ updatedAt="2024-06-26 02:20:58.689998+00:00",
body="Sample post content
",
ownerUserId=1,
- # ownerDisplayName="John Doe",
- # lastEditorUserId=1,
- # lastEditorDisplayName="Jane Doe",
- # lastEditDate="2024-01-02",
- # lastActivityDate="2024-01-03",
title="Sample Title",
- # answerCount=0,
- # commentCount=0,
- # favoriteCount=0,
- # closedDate=None,
- # communityOwnedDate=None,
)
- # Correctly assign the tag to the post
- self.sample_post.tags.set([self.sample_tag])
-
- # Create sample user
- user = User.objects.create_superuser("username")
- # Create sample user profile
- self.sample_user_profile = UserProfile.objects.create(
- user=user,
- username="username",
- avatar="avatar",
- bio="bio",
- reputation=1,
- joinDate="2024-01-01",
- upVotes=1,
- downVotes=1,
- )
- # Correctly assign the post to the user profile
- self.sample_user_profile.posts.set([self.sample_post])
- self.sample_user_profile.edits.set([self.sample_post])
- self.sample_user_profile.savedPosts.set([self.sample_post])
- self.sample_user_profile.private_post.set([self.sample_post])
-
- def test_tags_api(self):
- # Test the tags API endpoint
- response = self.client.get(reverse("tags-list"))
- self.assertEqual(response.status_code, 200)
- self.assertContains(response, self.sample_tag.tagName)
def test_posts_api(self):
# Test the posts API endpoint
response = self.client.get(reverse("posts-list"))
self.assertEqual(response.status_code, 200)
self.assertContains(response, self.sample_post.title)
-
- def test_user_profile_api(self):
- # Test the user profile API endpoint
- response = self.client.get(reverse("user-profile-list"))
- self.assertEqual(response.status_code, 200)
- self.assertContains(response, self.sample_user_profile.username)
-
- def test_alivecheck_smoketest(self):
- response = self.client.get(reverse("alive-list"))
- self.assertEqual(response.status_code, 200)
diff --git a/backend/core/tests/test_user.py b/backend/core/tests/test_user.py
new file mode 100644
index 0000000..3c3e480
--- /dev/null
+++ b/backend/core/tests/test_user.py
@@ -0,0 +1,43 @@
+from django.contrib.auth.models import User
+from django.test import Client, TestCase
+from django.urls import reverse
+
+from core.models import Posts, UserProfile
+
+
+class UserTests(TestCase):
+ def setUp(self):
+ self.client = Client()
+ user = User.objects.create_superuser("username")
+
+ self.sample_post = Posts.objects.create(
+ type="argument",
+ createdAt="2024-06-26 02:20:58.689998+00:00",
+ updatedAt="2024-06-26 02:20:58.689998+00:00",
+ body="Sample post content
",
+ ownerUserId=1,
+ title="Sample Title",
+ )
+
+ # Create sample user profile
+ self.sample_user_profile = UserProfile.objects.create(
+ user=user,
+ username="username",
+ avatar="avatar",
+ bio="bio",
+ reputation=1,
+ joinDate="2024-01-01",
+ upVotes=1,
+ downVotes=1,
+ )
+ # Correctly assign the post to the user profile
+ self.sample_user_profile.posts.set([self.sample_post])
+ self.sample_user_profile.edits.set([self.sample_post])
+ self.sample_user_profile.savedPosts.set([self.sample_post])
+ self.sample_user_profile.private_post.set([self.sample_post])
+
+ def test_user_profile_api(self):
+ # Test the user profile API endpoint
+ response = self.client.get(reverse("user-profile-list"))
+ self.assertEqual(response.status_code, 200)
+ self.assertContains(response, self.sample_user_profile.username)
diff --git a/backend/core/views.py b/backend/core/views.py
index 250d14d..d371e64 100644
--- a/backend/core/views.py
+++ b/backend/core/views.py
@@ -20,11 +20,10 @@
from rest_framework.response import Response
from .forms import UserRegisterForm
-from .models import Posts, Tags, UserProfile
+from .models import Posts, UserProfile
from .serializers import (
ArgumentSerializer,
PostSerializer,
- TagSerializer,
UserProfileSerializer,
)
from .token import account_activation_token
@@ -66,11 +65,6 @@ def get_permissions(self):
return [AllowAny()]
-class TagViewSet(viewsets.ModelViewSet):
- queryset = Tags.objects.all()
- serializer_class = TagSerializer
-
-
class PostViewSet(viewsets.ModelViewSet):
queryset = Posts.objects.all()
serializer_class = PostSerializer
diff --git a/backend/rebutify/urls.py b/backend/rebutify/urls.py
index 85d62f1..90e5e77 100644
--- a/backend/rebutify/urls.py
+++ b/backend/rebutify/urls.py
@@ -29,7 +29,6 @@
router = routers.DefaultRouter()
router.register(r"status/alive", views.StatusViewSet, basename="alive")
-router.register(r"tags", views.TagViewSet, basename="tags")
router.register(r"posts", views.PostViewSet, basename="posts")
router.register(r"arguments", views.ArgumentViewSet, basename="arguments")
router.register(r"user-profile", views.UserProfileViewSet, basename="user-profile")
From 9e1ecacd9460f440c81364961707fea058ca532b Mon Sep 17 00:00:00 2001
From: Ziggy
Date: Wed, 26 Jun 2024 04:37:18 +0200
Subject: [PATCH 16/18] fix: missing comma
---
backend/core/views.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/backend/core/views.py b/backend/core/views.py
index d371e64..cb99f32 100644
--- a/backend/core/views.py
+++ b/backend/core/views.py
@@ -73,7 +73,9 @@ def get_permissions(self):
if self.action == "create":
return [IsAuthenticated()]
if self.action in ["update", "delete", "partial_update"]:
- return [IsOwnerOrReadOnly() | IsAdminUser()]
+ return [
+ IsOwnerOrReadOnly() | IsAdminUser(),
+ ]
return [AllowAny()]
From 288b39204dd4a351c88635f666161f268dff2f86 Mon Sep 17 00:00:00 2001
From: Ziggy
Date: Wed, 26 Jun 2024 04:42:32 +0200
Subject: [PATCH 17/18] fix: IsOwnerOrReadOnly checks for userOwnerId
---
backend/core/views.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/backend/core/views.py b/backend/core/views.py
index cb99f32..0d8443b 100644
--- a/backend/core/views.py
+++ b/backend/core/views.py
@@ -40,8 +40,8 @@ def has_object_permission(self, request, view, obj):
# so we'll always allow GET, HEAD or OPTIONS requests.
if request.method in SAFE_METHODS:
return True
- # Instance must have an attribute named `owner`.
- return obj.owner == request.user
+ # Instance must have an attribute named `ownerUserId`.
+ return obj.ownerUserId == request.user
def success(request):
From e058bcb07061adc30c96dfd2a684bf585caba184 Mon Sep 17 00:00:00 2001
From: Ziggy
Date: Wed, 26 Jun 2024 04:56:47 +0200
Subject: [PATCH 18/18] fix: permission logic
---
backend/core/views.py | 11 +++--------
1 file changed, 3 insertions(+), 8 deletions(-)
diff --git a/backend/core/views.py b/backend/core/views.py
index 0d8443b..a5c8355 100644
--- a/backend/core/views.py
+++ b/backend/core/views.py
@@ -14,7 +14,6 @@
SAFE_METHODS,
AllowAny,
BasePermission,
- IsAdminUser,
IsAuthenticated,
)
from rest_framework.response import Response
@@ -58,10 +57,8 @@ def perform_create(self, serializer):
def get_permissions(self):
if self.action == "create":
return [IsAuthenticated()]
- if self.action == "update" or self.action == "delete":
- return [
- IsOwnerOrReadOnly() | IsAdminUser(),
- ]
+ if self.action in ["update", "delete", "partial_update"]:
+ return [IsOwnerOrReadOnly()]
return [AllowAny()]
@@ -73,9 +70,7 @@ def get_permissions(self):
if self.action == "create":
return [IsAuthenticated()]
if self.action in ["update", "delete", "partial_update"]:
- return [
- IsOwnerOrReadOnly() | IsAdminUser(),
- ]
+ return [IsOwnerOrReadOnly()]
return [AllowAny()]