Spaces:
Runtime error
Runtime error
An Hang
commited on
add like (#40)
Browse filesadd like and follow
- src/social/admin.py +2 -2
- src/social/forms.py +8 -9
- src/social/migrations/0001_initial.py +1 -11
- src/social/migrations/0002_comment.py +27 -0
- src/social/migrations/0003_userprofile.py +26 -0
- src/social/migrations/0004_userprofile_followers.py +20 -0
- src/social/migrations/0005_auto_20210131_1615.py +25 -0
- src/social/migrations/0006_alter_comment_id_alter_post_id.py +23 -0
- src/social/models.py +26 -5
- src/social/templates/social/comment_delete.html +14 -14
- src/social/templates/social/post_delete.html +14 -14
- src/social/templates/social/post_detail.html +27 -13
- src/social/templates/social/post_edit.html +9 -7
- src/social/templates/social/post_list.html +43 -22
- src/social/templates/social/profile.html +94 -0
- src/social/templates/social/profile_edit.html +30 -0
- src/social/urls.py +9 -1
- src/social/views.py +129 -9
src/social/admin.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
from django.contrib import admin
|
| 2 |
-
from .models import Post
|
| 3 |
|
| 4 |
admin.site.register(Post)
|
| 5 |
-
|
|
|
|
| 1 |
from django.contrib import admin
|
| 2 |
+
from .models import Post, UserProfile
|
| 3 |
|
| 4 |
admin.site.register(Post)
|
| 5 |
+
admin.site.register(UserProfile)
|
src/social/forms.py
CHANGED
|
@@ -1,14 +1,13 @@
|
|
| 1 |
from django import forms
|
| 2 |
from .models import Post, Comment
|
| 3 |
|
| 4 |
-
|
| 5 |
class PostForm(forms.ModelForm):
|
| 6 |
body = forms.CharField(
|
| 7 |
label='',
|
| 8 |
-
widget=forms.Textarea(
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
|
| 13 |
class Meta:
|
| 14 |
model = Post
|
|
@@ -17,10 +16,10 @@ class PostForm(forms.ModelForm):
|
|
| 17 |
class CommentForm(forms.ModelForm):
|
| 18 |
comment = forms.CharField(
|
| 19 |
label='',
|
| 20 |
-
widget=forms.Textarea(
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
|
| 25 |
class Meta:
|
| 26 |
model = Comment
|
|
|
|
| 1 |
from django import forms
|
| 2 |
from .models import Post, Comment
|
| 3 |
|
|
|
|
| 4 |
class PostForm(forms.ModelForm):
|
| 5 |
body = forms.CharField(
|
| 6 |
label='',
|
| 7 |
+
widget=forms.Textarea(attrs={
|
| 8 |
+
'rows': '3',
|
| 9 |
+
'placeholder': 'Say Something...'
|
| 10 |
+
}))
|
| 11 |
|
| 12 |
class Meta:
|
| 13 |
model = Post
|
|
|
|
| 16 |
class CommentForm(forms.ModelForm):
|
| 17 |
comment = forms.CharField(
|
| 18 |
label='',
|
| 19 |
+
widget=forms.Textarea(attrs={
|
| 20 |
+
'rows': '3',
|
| 21 |
+
'placeholder': 'Say Something...'
|
| 22 |
+
}))
|
| 23 |
|
| 24 |
class Meta:
|
| 25 |
model = Comment
|
src/social/migrations/0001_initial.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
# Generated by Django
|
| 2 |
|
| 3 |
from django.conf import settings
|
| 4 |
from django.db import migrations, models
|
|
@@ -24,14 +24,4 @@ class Migration(migrations.Migration):
|
|
| 24 |
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
| 25 |
],
|
| 26 |
),
|
| 27 |
-
migrations.CreateModel(
|
| 28 |
-
name='Comment',
|
| 29 |
-
fields=[
|
| 30 |
-
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
| 31 |
-
('comment', models.TextField()),
|
| 32 |
-
('created_on', models.DateTimeField(default=django.utils.timezone.now)),
|
| 33 |
-
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
| 34 |
-
('post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='social.post')),
|
| 35 |
-
],
|
| 36 |
-
),
|
| 37 |
]
|
|
|
|
| 1 |
+
# Generated by Django 2.1.4 on 2020-12-21 23:54
|
| 2 |
|
| 3 |
from django.conf import settings
|
| 4 |
from django.db import migrations, models
|
|
|
|
| 24 |
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
| 25 |
],
|
| 26 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
]
|
src/social/migrations/0002_comment.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Generated by Django 3.1.4 on 2021-01-01 18:58
|
| 2 |
+
|
| 3 |
+
from django.conf import settings
|
| 4 |
+
from django.db import migrations, models
|
| 5 |
+
import django.db.models.deletion
|
| 6 |
+
import django.utils.timezone
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class Migration(migrations.Migration):
|
| 10 |
+
|
| 11 |
+
dependencies = [
|
| 12 |
+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
| 13 |
+
('social', '0001_initial'),
|
| 14 |
+
]
|
| 15 |
+
|
| 16 |
+
operations = [
|
| 17 |
+
migrations.CreateModel(
|
| 18 |
+
name='Comment',
|
| 19 |
+
fields=[
|
| 20 |
+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
| 21 |
+
('comment', models.TextField()),
|
| 22 |
+
('created_on', models.DateTimeField(default=django.utils.timezone.now)),
|
| 23 |
+
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
| 24 |
+
('post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='social.post')),
|
| 25 |
+
],
|
| 26 |
+
),
|
| 27 |
+
]
|
src/social/migrations/0003_userprofile.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Generated by Django 3.1.4 on 2021-01-17 18:24
|
| 2 |
+
|
| 3 |
+
from django.db import migrations, models
|
| 4 |
+
import django.db.models.deletion
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class Migration(migrations.Migration):
|
| 8 |
+
|
| 9 |
+
dependencies = [
|
| 10 |
+
('auth', '0012_alter_user_first_name_max_length'),
|
| 11 |
+
('social', '0002_comment'),
|
| 12 |
+
]
|
| 13 |
+
|
| 14 |
+
operations = [
|
| 15 |
+
migrations.CreateModel(
|
| 16 |
+
name='UserProfile',
|
| 17 |
+
fields=[
|
| 18 |
+
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='profile', serialize=False, to='auth.user', verbose_name='user')),
|
| 19 |
+
('name', models.CharField(blank=True, max_length=30, null=True)),
|
| 20 |
+
('bio', models.TextField(blank=True, max_length=500, null=True)),
|
| 21 |
+
('birth_date', models.DateField(blank=True, null=True)),
|
| 22 |
+
('location', models.CharField(blank=True, max_length=100, null=True)),
|
| 23 |
+
('picture', models.ImageField(blank=True, default='uploads/profile_pictures/default.png', upload_to='uploads/profile_pictures')),
|
| 24 |
+
],
|
| 25 |
+
),
|
| 26 |
+
]
|
src/social/migrations/0004_userprofile_followers.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Generated by Django 3.1.4 on 2021-01-26 00:21
|
| 2 |
+
|
| 3 |
+
from django.conf import settings
|
| 4 |
+
from django.db import migrations, models
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class Migration(migrations.Migration):
|
| 8 |
+
|
| 9 |
+
dependencies = [
|
| 10 |
+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
| 11 |
+
('social', '0003_userprofile'),
|
| 12 |
+
]
|
| 13 |
+
|
| 14 |
+
operations = [
|
| 15 |
+
migrations.AddField(
|
| 16 |
+
model_name='userprofile',
|
| 17 |
+
name='followers',
|
| 18 |
+
field=models.ManyToManyField(blank=True, related_name='followers', to=settings.AUTH_USER_MODEL),
|
| 19 |
+
),
|
| 20 |
+
]
|
src/social/migrations/0005_auto_20210131_1615.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Generated by Django 3.1.4 on 2021-01-31 16:15
|
| 2 |
+
|
| 3 |
+
from django.conf import settings
|
| 4 |
+
from django.db import migrations, models
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class Migration(migrations.Migration):
|
| 8 |
+
|
| 9 |
+
dependencies = [
|
| 10 |
+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
| 11 |
+
('social', '0004_userprofile_followers'),
|
| 12 |
+
]
|
| 13 |
+
|
| 14 |
+
operations = [
|
| 15 |
+
migrations.AddField(
|
| 16 |
+
model_name='post',
|
| 17 |
+
name='dislikes',
|
| 18 |
+
field=models.ManyToManyField(blank=True, related_name='dislikes', to=settings.AUTH_USER_MODEL),
|
| 19 |
+
),
|
| 20 |
+
migrations.AddField(
|
| 21 |
+
model_name='post',
|
| 22 |
+
name='likes',
|
| 23 |
+
field=models.ManyToManyField(blank=True, related_name='likes', to=settings.AUTH_USER_MODEL),
|
| 24 |
+
),
|
| 25 |
+
]
|
src/social/migrations/0006_alter_comment_id_alter_post_id.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Generated by Django 5.0.3 on 2024-04-01 10:12
|
| 2 |
+
|
| 3 |
+
from django.db import migrations, models
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class Migration(migrations.Migration):
|
| 7 |
+
|
| 8 |
+
dependencies = [
|
| 9 |
+
('social', '0005_auto_20210131_1615'),
|
| 10 |
+
]
|
| 11 |
+
|
| 12 |
+
operations = [
|
| 13 |
+
migrations.AlterField(
|
| 14 |
+
model_name='comment',
|
| 15 |
+
name='id',
|
| 16 |
+
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
|
| 17 |
+
),
|
| 18 |
+
migrations.AlterField(
|
| 19 |
+
model_name='post',
|
| 20 |
+
name='id',
|
| 21 |
+
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
|
| 22 |
+
),
|
| 23 |
+
]
|
src/social/models.py
CHANGED
|
@@ -1,16 +1,37 @@
|
|
| 1 |
from django.db import models
|
| 2 |
from django.utils import timezone
|
| 3 |
from django.contrib.auth.models import User
|
|
|
|
|
|
|
| 4 |
|
| 5 |
|
| 6 |
class Post(models.Model):
|
| 7 |
body = models.TextField()
|
| 8 |
created_on = models.DateTimeField(default=timezone.now)
|
| 9 |
author = models.ForeignKey(User, on_delete=models.CASCADE)
|
| 10 |
-
|
|
|
|
| 11 |
|
| 12 |
class Comment(models.Model):
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
from django.db import models
|
| 2 |
from django.utils import timezone
|
| 3 |
from django.contrib.auth.models import User
|
| 4 |
+
from django.db.models.signals import post_save
|
| 5 |
+
from django.dispatch import receiver
|
| 6 |
|
| 7 |
|
| 8 |
class Post(models.Model):
|
| 9 |
body = models.TextField()
|
| 10 |
created_on = models.DateTimeField(default=timezone.now)
|
| 11 |
author = models.ForeignKey(User, on_delete=models.CASCADE)
|
| 12 |
+
likes = models.ManyToManyField(User, blank=True, related_name='likes')
|
| 13 |
+
dislikes = models.ManyToManyField(User, blank=True, related_name='dislikes')
|
| 14 |
|
| 15 |
class Comment(models.Model):
|
| 16 |
+
comment = models.TextField()
|
| 17 |
+
created_on = models.DateTimeField(default=timezone.now)
|
| 18 |
+
author = models.ForeignKey(User, on_delete=models.CASCADE)
|
| 19 |
+
post = models.ForeignKey('Post', on_delete=models.CASCADE)
|
| 20 |
+
|
| 21 |
+
class UserProfile(models.Model):
|
| 22 |
+
user = models.OneToOneField(User, primary_key=True, verbose_name='user', related_name='profile', on_delete=models.CASCADE)
|
| 23 |
+
name = models.CharField(max_length=30, blank=True, null=True)
|
| 24 |
+
bio = models.TextField(max_length=500, blank=True, null=True)
|
| 25 |
+
birth_date=models.DateField(null=True, blank=True)
|
| 26 |
+
location = models.CharField(max_length=100, blank=True, null=True)
|
| 27 |
+
picture = models.ImageField(upload_to='uploads/profile_pictures', default='uploads/profile_pictures/default.png', blank=True)
|
| 28 |
+
followers = models.ManyToManyField(User, blank=True, related_name='followers')
|
| 29 |
+
|
| 30 |
+
@receiver(post_save, sender=User)
|
| 31 |
+
def create_user_profile(sender, instance, created, **kwargs):
|
| 32 |
+
if created:
|
| 33 |
+
UserProfile.objects.create(user=instance)
|
| 34 |
+
|
| 35 |
+
@receiver(post_save, sender=User)
|
| 36 |
+
def save_user_profile(sender, instance, **kwargs):
|
| 37 |
+
instance.profile.save()
|
src/social/templates/social/comment_delete.html
CHANGED
|
@@ -1,24 +1,24 @@
|
|
| 1 |
{% extends 'landing/base.html' %}
|
| 2 |
-
{% load crispy_forms_tags %}
|
| 3 |
|
| 4 |
{% block content %}
|
| 5 |
<div class="container">
|
| 6 |
<div class="row mt-5">
|
| 7 |
-
<div class="col-md-
|
| 8 |
-
<a href="{% url 'post-detail' object.
|
| 9 |
</div>
|
| 10 |
</div>
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
<
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
<
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
|
|
|
| 22 |
</div>
|
| 23 |
</div>
|
| 24 |
{% endblock content %}
|
|
|
|
| 1 |
{% extends 'landing/base.html' %}
|
|
|
|
| 2 |
|
| 3 |
{% block content %}
|
| 4 |
<div class="container">
|
| 5 |
<div class="row mt-5">
|
| 6 |
+
<div class="col-md-5 col-sm-6">
|
| 7 |
+
<a href="{% url 'post-detail' object.pk %}" class="btn btn-light">Back To Post</a>
|
| 8 |
</div>
|
| 9 |
</div>
|
| 10 |
+
|
| 11 |
+
<div class="row justify-content-center mt-3 mb-5">
|
| 12 |
+
<div class="col-md-5 col-sm-12 border-bottom">
|
| 13 |
+
<form method="POST">
|
| 14 |
+
{% csrf_token %}
|
| 15 |
+
<h5>Are You Sure?</h5>
|
| 16 |
+
<p>You are about to delete this comment, this cannot be undone.</p>
|
| 17 |
+
<div class="d-grid gap-2">
|
| 18 |
+
<button class="btn btn-danger mt-3">Delete</button>
|
| 19 |
+
</div>
|
| 20 |
+
</form>
|
| 21 |
+
</div>
|
| 22 |
</div>
|
| 23 |
</div>
|
| 24 |
{% endblock content %}
|
src/social/templates/social/post_delete.html
CHANGED
|
@@ -1,24 +1,24 @@
|
|
| 1 |
{% extends 'landing/base.html' %}
|
| 2 |
-
{% load crispy_forms_tags %}
|
| 3 |
|
| 4 |
{% block content %}
|
| 5 |
<div class="container">
|
| 6 |
<div class="row mt-5">
|
| 7 |
-
<div class="col-md-
|
| 8 |
-
<a href="{% url 'post-detail' object.pk %}" class="btn btn-light">Back
|
| 9 |
</div>
|
| 10 |
</div>
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
<
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
<
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
|
|
|
| 22 |
</div>
|
| 23 |
</div>
|
| 24 |
{% endblock content %}
|
|
|
|
| 1 |
{% extends 'landing/base.html' %}
|
|
|
|
| 2 |
|
| 3 |
{% block content %}
|
| 4 |
<div class="container">
|
| 5 |
<div class="row mt-5">
|
| 6 |
+
<div class="col-md-5 col-sm-6">
|
| 7 |
+
<a href="{% url 'post-detail' object.pk %}" class="btn btn-light">Back To Post</a>
|
| 8 |
</div>
|
| 9 |
</div>
|
| 10 |
+
|
| 11 |
+
<div class="row justify-content-center mt-3 mb-5">
|
| 12 |
+
<div class="col-md-5 col-sm-12 border-bottom">
|
| 13 |
+
<form method="POST">
|
| 14 |
+
{% csrf_token %}
|
| 15 |
+
<h5>Are You Sure?</h5>
|
| 16 |
+
<p>You are about to delete this post, this cannot be undone.</p>
|
| 17 |
+
<div class="d-grid gap-2">
|
| 18 |
+
<button class="btn btn-danger mt-3">Delete</button>
|
| 19 |
+
</div>
|
| 20 |
+
</form>
|
| 21 |
+
</div>
|
| 22 |
</div>
|
| 23 |
</div>
|
| 24 |
{% endblock content %}
|
src/social/templates/social/post_detail.html
CHANGED
|
@@ -4,28 +4,42 @@
|
|
| 4 |
{% block content %}
|
| 5 |
<div class="container">
|
| 6 |
<div class="row mt-5">
|
| 7 |
-
<div class="col-md-
|
| 8 |
-
<a href="{% url 'post-list' %}" class="btn btn-light">Back
|
| 9 |
</div>
|
| 10 |
</div>
|
|
|
|
| 11 |
<div class="row justify-content-center mt-3">
|
| 12 |
<div class="col-md-5 col-sm-12 border-bottom">
|
| 13 |
<p>
|
| 14 |
<strong>{{ post.author }}</strong> {{ post.created_on }}
|
| 15 |
{% if request.user == post.author %}
|
| 16 |
-
|
| 17 |
-
|
| 18 |
{% endif %}
|
| 19 |
</p>
|
| 20 |
<p>{{ post.body }}</p>
|
| 21 |
-
</div>
|
| 22 |
-
</div>
|
| 23 |
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
</div>
|
| 28 |
</div>
|
|
|
|
| 29 |
<div class="row justify-content-center mt-3 mb-5">
|
| 30 |
<div class="col-md-5 col-sm-12">
|
| 31 |
<form method="POST">
|
|
@@ -38,12 +52,12 @@
|
|
| 38 |
</div>
|
| 39 |
</div>
|
| 40 |
{% for comment in comments %}
|
| 41 |
-
<div class="row justify-content-center mt-3 mb-5
|
| 42 |
-
<div class="col-md-5 col-sm-12">
|
| 43 |
<p>
|
| 44 |
<strong>{{ comment.author }}</strong> {{ comment.created_on }}
|
| 45 |
{% if request.user == comment.author %}
|
| 46 |
-
|
| 47 |
{% endif %}
|
| 48 |
</p>
|
| 49 |
<p>{{ comment.comment }}</p>
|
|
@@ -51,4 +65,4 @@
|
|
| 51 |
</div>
|
| 52 |
{% endfor %}
|
| 53 |
</div>
|
| 54 |
-
{% endblock content %}
|
|
|
|
| 4 |
{% block content %}
|
| 5 |
<div class="container">
|
| 6 |
<div class="row mt-5">
|
| 7 |
+
<div class="col-md-5 col-sm-6">
|
| 8 |
+
<a href="{% url 'post-list' %}" class="btn btn-light">Back To Feed</a>
|
| 9 |
</div>
|
| 10 |
</div>
|
| 11 |
+
|
| 12 |
<div class="row justify-content-center mt-3">
|
| 13 |
<div class="col-md-5 col-sm-12 border-bottom">
|
| 14 |
<p>
|
| 15 |
<strong>{{ post.author }}</strong> {{ post.created_on }}
|
| 16 |
{% if request.user == post.author %}
|
| 17 |
+
<a href="{% url 'post-edit' post.pk %}" style="color: #333;"><i class="far fa-edit"></i></a>
|
| 18 |
+
<a href="{% url 'post-delete' post.pk %}" style="color: #333;"><i class="fas fa-trash"></i></a>
|
| 19 |
{% endif %}
|
| 20 |
</p>
|
| 21 |
<p>{{ post.body }}</p>
|
|
|
|
|
|
|
| 22 |
|
| 23 |
+
<div class="d-flex flex-row">
|
| 24 |
+
<form method="POST" action="{% url 'like' post.pk %}">
|
| 25 |
+
{% csrf_token %}
|
| 26 |
+
<input type="hidden" name="next" value="{{ request.path }}">
|
| 27 |
+
<button style="background-color: transparent; border: none; box-shadow: none;" type="submit">
|
| 28 |
+
<i class="far fa-thumbs-up"> <span>{{ post.likes.all.count }}</span></i>
|
| 29 |
+
</button>
|
| 30 |
+
</form>
|
| 31 |
+
|
| 32 |
+
<form method="POST" action="{% url 'dislike' post.pk %}">
|
| 33 |
+
{% csrf_token %}
|
| 34 |
+
<input type="hidden" name="next" value="{{ request.path }}">
|
| 35 |
+
<button style="background-color: transparent; border: none; box-shadow: none;" type="submit">
|
| 36 |
+
<i class="far fa-thumbs-down"> <span>{{ post.dislikes.all.count }}</span></i>
|
| 37 |
+
</button>
|
| 38 |
+
</form>
|
| 39 |
+
</div>
|
| 40 |
</div>
|
| 41 |
</div>
|
| 42 |
+
|
| 43 |
<div class="row justify-content-center mt-3 mb-5">
|
| 44 |
<div class="col-md-5 col-sm-12">
|
| 45 |
<form method="POST">
|
|
|
|
| 52 |
</div>
|
| 53 |
</div>
|
| 54 |
{% for comment in comments %}
|
| 55 |
+
<div class="row justify-content-center mt-3 mb-5">
|
| 56 |
+
<div class="col-md-5 col-sm-12 border-bottom">
|
| 57 |
<p>
|
| 58 |
<strong>{{ comment.author }}</strong> {{ comment.created_on }}
|
| 59 |
{% if request.user == comment.author %}
|
| 60 |
+
<a href="{% url 'comment-delete' post.pk comment.pk %}" style="color: #333;"><i class="fas fa-trash"></i></a>
|
| 61 |
{% endif %}
|
| 62 |
</p>
|
| 63 |
<p>{{ comment.comment }}</p>
|
|
|
|
| 65 |
</div>
|
| 66 |
{% endfor %}
|
| 67 |
</div>
|
| 68 |
+
{% endblock content %}
|
src/social/templates/social/post_edit.html
CHANGED
|
@@ -4,17 +4,19 @@
|
|
| 4 |
{% block content %}
|
| 5 |
<div class="container">
|
| 6 |
<div class="row mt-5">
|
| 7 |
-
<div class="col-md-
|
| 8 |
-
<a href="{% url 'post-detail' object.pk %}" class="btn btn-light">Back
|
| 9 |
</div>
|
| 10 |
</div>
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
|
|
|
| 14 |
</div>
|
| 15 |
</div>
|
|
|
|
| 16 |
<div class="row justify-content-center mt-3 mb-5">
|
| 17 |
-
<div class="col-md-5 col-sm-12">
|
| 18 |
<form method="POST">
|
| 19 |
{% csrf_token %}
|
| 20 |
{{ form | crispy }}
|
|
@@ -25,4 +27,4 @@
|
|
| 25 |
</div>
|
| 26 |
</div>
|
| 27 |
</div>
|
| 28 |
-
{% endblock content %}
|
|
|
|
| 4 |
{% block content %}
|
| 5 |
<div class="container">
|
| 6 |
<div class="row mt-5">
|
| 7 |
+
<div class="col-md-5 col-sm-6">
|
| 8 |
+
<a href="{% url 'post-detail' object.pk %}" class="btn btn-light">Back To Post</a>
|
| 9 |
</div>
|
| 10 |
</div>
|
| 11 |
+
|
| 12 |
+
<div class="row justify-content-center mt-3">
|
| 13 |
+
<div class="col-md-5 col-sm-12 border-bottom">
|
| 14 |
+
<h5>Update Your Post!</h5>
|
| 15 |
</div>
|
| 16 |
</div>
|
| 17 |
+
|
| 18 |
<div class="row justify-content-center mt-3 mb-5">
|
| 19 |
+
<div class="col-md-5 col-sm-12 border-bottom">
|
| 20 |
<form method="POST">
|
| 21 |
{% csrf_token %}
|
| 22 |
{{ form | crispy }}
|
|
|
|
| 27 |
</div>
|
| 28 |
</div>
|
| 29 |
</div>
|
| 30 |
+
{% endblock content %}
|
src/social/templates/social/post_list.html
CHANGED
|
@@ -2,31 +2,52 @@
|
|
| 2 |
{% load crispy_forms_tags %}
|
| 3 |
|
| 4 |
{% block content %}
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
</div>
|
| 10 |
</div>
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
|
|
|
| 21 |
</div>
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
|
|
|
|
|
|
|
|
|
| 26 |
<p>{{ post.body }}</p>
|
| 27 |
-
<a
|
| 28 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
</div>
|
| 30 |
-
{% endfor %}
|
| 31 |
</div>
|
| 32 |
-
{%
|
|
|
|
|
|
|
|
|
| 2 |
{% load crispy_forms_tags %}
|
| 3 |
|
| 4 |
{% block content %}
|
| 5 |
+
<div class="container">
|
| 6 |
+
<div class="row justify-content-center mt-3">
|
| 7 |
+
<div class="col-md-5 col-sm-12 border-bottom">
|
| 8 |
+
<h5>Add a Post!</h5>
|
|
|
|
| 9 |
</div>
|
| 10 |
+
</div>
|
| 11 |
+
|
| 12 |
+
<div class="row justify-content-center mt-3 mb-5">
|
| 13 |
+
<div class="col-md-5 col-sm-12 border-bottom">
|
| 14 |
+
<form method="POST">
|
| 15 |
+
{% csrf_token %}
|
| 16 |
+
{{ form | crispy }}
|
| 17 |
+
<div class="d-grid gap-2">
|
| 18 |
+
<button class="btn btn-success mt-3">Submit!</button>
|
| 19 |
+
</div>
|
| 20 |
+
</form>
|
| 21 |
</div>
|
| 22 |
+
</div>
|
| 23 |
+
|
| 24 |
+
{% for post in post_list %}
|
| 25 |
+
<div class="row justify-content-center mt-3">
|
| 26 |
+
<div class="col-md-5 col-sm-12 border-bottom position-relative">
|
| 27 |
+
<p><a style="text-decoration: none" class="text-primary" href="{% url 'profile' post.author.profile.pk %}">@{{ post.author }}</a> {{ post.created_on }}</p>
|
| 28 |
+
<div class="position-relative">
|
| 29 |
<p>{{ post.body }}</p>
|
| 30 |
+
<a href="{% url 'post-detail' post.pk %}" class="stretched-link"></a>
|
| 31 |
</div>
|
| 32 |
+
|
| 33 |
+
<div class="d-flex flex-row">
|
| 34 |
+
<form method="POST" action="{% url 'like' post.pk %}">
|
| 35 |
+
{% csrf_token %}
|
| 36 |
+
<input type="hidden" name="next" value="{{ request.path }}">
|
| 37 |
+
<button style="background-color: transparent; border: none; box-shadow: none;" type="submit">
|
| 38 |
+
<i class="far fa-thumbs-up"> <span>{{ post.likes.all.count }}</span></i>
|
| 39 |
+
</button>
|
| 40 |
+
</form>
|
| 41 |
+
|
| 42 |
+
<form method="POST" action="{% url 'dislike' post.pk %}">
|
| 43 |
+
{% csrf_token %}
|
| 44 |
+
<input type="hidden" name="next" value="{{ request.path }}">
|
| 45 |
+
<button style="background-color: transparent; border: none; box-shadow: none;" type="submit">
|
| 46 |
+
<i class="far fa-thumbs-down"> <span>{{ post.dislikes.all.count }}</span></i>
|
| 47 |
+
</button>
|
| 48 |
+
</form>
|
| 49 |
</div>
|
|
|
|
| 50 |
</div>
|
| 51 |
+
{% endfor %}
|
| 52 |
+
</div>
|
| 53 |
+
{% endblock content %}
|
src/social/templates/social/profile.html
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{% extends 'landing/base.html' %}
|
| 2 |
+
|
| 3 |
+
{% block content %}
|
| 4 |
+
<div class="container">
|
| 5 |
+
<div class="row mt-5">
|
| 6 |
+
<div class="col-md-3 col-sm-6">
|
| 7 |
+
<a href="{% url 'post-list' %}" class="btn btn-light">Back to Feed</a>
|
| 8 |
+
</div>
|
| 9 |
+
</div>
|
| 10 |
+
|
| 11 |
+
<div class="row justify-content-center mt-5">
|
| 12 |
+
<div class="card shadow-sm col-md-8 col-sm-12 border-bottom px-5 pt-3">
|
| 13 |
+
<img src="{{ profile.picture.url }}" class="rounded-circle" width="100" height="100" />
|
| 14 |
+
{% if profile.name %}
|
| 15 |
+
<h3 class="py-4">{{ profile.name }}
|
| 16 |
+
<span>
|
| 17 |
+
{% if request.user == user %}
|
| 18 |
+
<a href="{% url 'profile-edit' profile.pk %}" style="color: #333"><i class="far fa-edit"></i></a>
|
| 19 |
+
{% endif %}
|
| 20 |
+
</span>
|
| 21 |
+
{% else %}
|
| 22 |
+
<h3 class="py-4">{{ user.username }}
|
| 23 |
+
<span>
|
| 24 |
+
{% if request.user == user %}
|
| 25 |
+
<a href="{% url 'profile-edit' profile.pk %}" style="color: #333"><i class="far fa-edit"></i></a>
|
| 26 |
+
{% endif %}
|
| 27 |
+
</span>
|
| 28 |
+
</h3>
|
| 29 |
+
{% endif %}
|
| 30 |
+
</h3>
|
| 31 |
+
|
| 32 |
+
{% if profile.location %}
|
| 33 |
+
<p>{{ profile.location }}</p>
|
| 34 |
+
{% endif %}
|
| 35 |
+
|
| 36 |
+
{% if profile.birth_date %}
|
| 37 |
+
<p>{{ profile.birth_date }}</p>
|
| 38 |
+
{% endif %}
|
| 39 |
+
|
| 40 |
+
{% if profile.bio %}
|
| 41 |
+
<p>{{ profile.bio }}</p>
|
| 42 |
+
{% endif %}
|
| 43 |
+
|
| 44 |
+
<div class="mb-3">
|
| 45 |
+
<p>Followers: {{ number_of_followers }}</p>
|
| 46 |
+
{% if user == request.user %}
|
| 47 |
+
{% else %}
|
| 48 |
+
{% if is_following %}
|
| 49 |
+
<form method="POST" action="{% url 'remove-follower' profile.pk %}">
|
| 50 |
+
{% csrf_token %}
|
| 51 |
+
<button class="btn btn-outline-danger" type="submit">Unfollow</button>
|
| 52 |
+
</form>
|
| 53 |
+
{% else %}
|
| 54 |
+
<form method="POST" action="{% url 'add-follower' profile.pk %}">
|
| 55 |
+
{% csrf_token %}
|
| 56 |
+
<button class="btn btn-outline-success" type="submit">Follow</button>
|
| 57 |
+
</form>
|
| 58 |
+
{% endif %}
|
| 59 |
+
{% endif %}
|
| 60 |
+
</div>
|
| 61 |
+
</div>
|
| 62 |
+
</div>
|
| 63 |
+
|
| 64 |
+
{% for post in posts %}
|
| 65 |
+
<div class="row justify-content-center mt-5">
|
| 66 |
+
<div class="col-md-8 col-sm-12 border-bottom">
|
| 67 |
+
<p><a style="text-decoration: none" class="text-primary" href="{% url 'profile' post.author.profile.pk %}">@{{ post.author }}</a> {{ post.created_on }}</p>
|
| 68 |
+
<div class="position-relative">
|
| 69 |
+
<p>{{ post.body }}</p>
|
| 70 |
+
<a href="{% url 'post-detail' post.pk %}" class="stretched-link"></a>
|
| 71 |
+
</div>
|
| 72 |
+
|
| 73 |
+
<div class="d-flex flex-row">
|
| 74 |
+
<form method="POST" action="{% url 'like' post.pk %}">
|
| 75 |
+
{% csrf_token %}
|
| 76 |
+
<input type="hidden" name="next" value="{{ request.path }}">
|
| 77 |
+
<button style="background-color: transparent; border: none; box-shadow: none;" type="submit">
|
| 78 |
+
<i class="far fa-thumbs-up"> <span>{{ post.likes.all.count }}</span></i>
|
| 79 |
+
</button>
|
| 80 |
+
</form>
|
| 81 |
+
|
| 82 |
+
<form method="POST" action="{% url 'dislike' post.pk %}">
|
| 83 |
+
{% csrf_token %}
|
| 84 |
+
<input type="hidden" name="next" value="{{ request.path }}">
|
| 85 |
+
<button style="background-color: transparent; border: none; box-shadow: none;" type="submit">
|
| 86 |
+
<i class="far fa-thumbs-down"> <span>{{ post.dislikes.all.count }}</span></i>
|
| 87 |
+
</button>
|
| 88 |
+
</form>
|
| 89 |
+
</div>
|
| 90 |
+
</div>
|
| 91 |
+
</div>
|
| 92 |
+
{% endfor %}
|
| 93 |
+
</div>
|
| 94 |
+
{% endblock content %}
|
src/social/templates/social/profile_edit.html
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{% extends 'landing/base.html' %}
|
| 2 |
+
{% load crispy_forms_tags %}
|
| 3 |
+
|
| 4 |
+
{% block content %}
|
| 5 |
+
<div class="container">
|
| 6 |
+
<div class="row mt-5">
|
| 7 |
+
<div class="col-md-5 col-sm-6">
|
| 8 |
+
<a href="{% url 'profile' object.pk %}" class="btn btn-light">Back To Profile</a>
|
| 9 |
+
</div>
|
| 10 |
+
</div>
|
| 11 |
+
|
| 12 |
+
<div class="row justify-content-center mt-3">
|
| 13 |
+
<div class="col-md-5 col-sm-12 border-bottom">
|
| 14 |
+
<h5>Update Your Profile</h5>
|
| 15 |
+
</div>
|
| 16 |
+
</div>
|
| 17 |
+
|
| 18 |
+
<div class="row justify-content-center mt-3 mb-5">
|
| 19 |
+
<div class="col-md-5 col-sm-12 border-bottom">
|
| 20 |
+
<form method="POST" enctype="multipart/form-data">
|
| 21 |
+
{% csrf_token %}
|
| 22 |
+
{{ form | crispy }}
|
| 23 |
+
<div class="d-grid gap-2">
|
| 24 |
+
<button class="btn btn-success mt-3">Submit!</button>
|
| 25 |
+
</div>
|
| 26 |
+
</form>
|
| 27 |
+
</div>
|
| 28 |
+
</div>
|
| 29 |
+
</div>
|
| 30 |
+
{% endblock content %}
|
src/social/urls.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
from django.urls import path
|
| 2 |
-
from .views import PostListView, PostDetailView, PostEditView, PostDeleteView, CommentDeleteView
|
| 3 |
|
| 4 |
urlpatterns = [
|
| 5 |
path('', PostListView.as_view(), name='post-list'),
|
|
@@ -7,4 +7,12 @@ urlpatterns = [
|
|
| 7 |
path('post/edit/<int:pk>/', PostEditView.as_view(), name='post-edit'),
|
| 8 |
path('post/delete/<int:pk>/', PostDeleteView.as_view(), name='post-delete'),
|
| 9 |
path('post/<int:post_pk>/comment/delete/<int:pk>/', CommentDeleteView.as_view(), name='comment-delete'),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
]
|
|
|
|
| 1 |
from django.urls import path
|
| 2 |
+
from .views import PostListView, PostDetailView, PostEditView, PostDeleteView, CommentDeleteView, ProfileView, ProfileEditView, AddFollower, RemoveFollower, AddLike, AddDislike
|
| 3 |
|
| 4 |
urlpatterns = [
|
| 5 |
path('', PostListView.as_view(), name='post-list'),
|
|
|
|
| 7 |
path('post/edit/<int:pk>/', PostEditView.as_view(), name='post-edit'),
|
| 8 |
path('post/delete/<int:pk>/', PostDeleteView.as_view(), name='post-delete'),
|
| 9 |
path('post/<int:post_pk>/comment/delete/<int:pk>/', CommentDeleteView.as_view(), name='comment-delete'),
|
| 10 |
+
path('post/<int:pk>/like', AddLike.as_view(), name='like'),
|
| 11 |
+
path('post/<int:pk>/dislike', AddDislike.as_view(), name='dislike'),
|
| 12 |
+
path('profile/<int:pk>/', ProfileView.as_view(), name='profile'),
|
| 13 |
+
path('profile/edit/<int:pk>/', ProfileEditView.as_view(), name='profile-edit'),
|
| 14 |
+
path('profile/<int:pk>/followers/add', AddFollower.as_view(), name='add-follower'),
|
| 15 |
+
path('profile/<int:pk>/followers/remove', RemoveFollower.as_view(), name='remove-follower'),
|
| 16 |
+
|
| 17 |
+
|
| 18 |
]
|
src/social/views.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
| 1 |
-
from django.shortcuts import render
|
| 2 |
from django.urls import reverse_lazy
|
|
|
|
| 3 |
from django.contrib.auth.mixins import UserPassesTestMixin, LoginRequiredMixin
|
| 4 |
from django.views import View
|
| 5 |
-
from
|
| 6 |
-
from .models import Post, Comment
|
| 7 |
from .forms import PostForm, CommentForm
|
|
|
|
| 8 |
|
| 9 |
|
| 10 |
class PostListView(LoginRequiredMixin, View):
|
|
@@ -16,6 +17,7 @@ class PostListView(LoginRequiredMixin, View):
|
|
| 16 |
'post_list': posts,
|
| 17 |
'form': form,
|
| 18 |
}
|
|
|
|
| 19 |
return render(request, 'social/post_list.html', context)
|
| 20 |
|
| 21 |
def post(self, request, *args, **kwargs):
|
|
@@ -31,12 +33,14 @@ class PostListView(LoginRequiredMixin, View):
|
|
| 31 |
'post_list': posts,
|
| 32 |
'form': form,
|
| 33 |
}
|
|
|
|
| 34 |
return render(request, 'social/post_list.html', context)
|
| 35 |
|
| 36 |
class PostDetailView(LoginRequiredMixin, View):
|
| 37 |
def get(self, request, pk, *args, **kwargs):
|
| 38 |
post = Post.objects.get(pk=pk)
|
| 39 |
form = CommentForm()
|
|
|
|
| 40 |
comments = Comment.objects.filter(post=post).order_by('-created_on')
|
| 41 |
|
| 42 |
context = {
|
|
@@ -46,7 +50,6 @@ class PostDetailView(LoginRequiredMixin, View):
|
|
| 46 |
}
|
| 47 |
|
| 48 |
return render(request, 'social/post_detail.html', context)
|
| 49 |
-
|
| 50 |
def post(self, request, pk, *args, **kwargs):
|
| 51 |
post = Post.objects.get(pk=pk)
|
| 52 |
form = CommentForm(request.POST)
|
|
@@ -56,7 +59,7 @@ class PostDetailView(LoginRequiredMixin, View):
|
|
| 56 |
new_comment.author = request.user
|
| 57 |
new_comment.post = post
|
| 58 |
new_comment.save()
|
| 59 |
-
|
| 60 |
comments = Comment.objects.filter(post=post).order_by('-created_on')
|
| 61 |
|
| 62 |
context = {
|
|
@@ -71,11 +74,11 @@ class PostEditView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
|
|
| 71 |
model = Post
|
| 72 |
fields = ['body']
|
| 73 |
template_name = 'social/post_edit.html'
|
| 74 |
-
|
| 75 |
def get_success_url(self):
|
| 76 |
pk = self.kwargs['pk']
|
| 77 |
return reverse_lazy('post-detail', kwargs={'pk': pk})
|
| 78 |
-
|
| 79 |
def test_func(self):
|
| 80 |
post = self.get_object()
|
| 81 |
return self.request.user == post.author
|
|
@@ -98,5 +101,122 @@ class CommentDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
|
|
| 98 |
return reverse_lazy('post-detail', kwargs={'pk': pk})
|
| 99 |
|
| 100 |
def test_func(self):
|
| 101 |
-
|
| 102 |
-
return self.request.user ==
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from django.shortcuts import render, redirect
|
| 2 |
from django.urls import reverse_lazy
|
| 3 |
+
from django.http import HttpResponseRedirect
|
| 4 |
from django.contrib.auth.mixins import UserPassesTestMixin, LoginRequiredMixin
|
| 5 |
from django.views import View
|
| 6 |
+
from .models import Post, Comment, UserProfile
|
|
|
|
| 7 |
from .forms import PostForm, CommentForm
|
| 8 |
+
from django.views.generic.edit import UpdateView, DeleteView
|
| 9 |
|
| 10 |
|
| 11 |
class PostListView(LoginRequiredMixin, View):
|
|
|
|
| 17 |
'post_list': posts,
|
| 18 |
'form': form,
|
| 19 |
}
|
| 20 |
+
|
| 21 |
return render(request, 'social/post_list.html', context)
|
| 22 |
|
| 23 |
def post(self, request, *args, **kwargs):
|
|
|
|
| 33 |
'post_list': posts,
|
| 34 |
'form': form,
|
| 35 |
}
|
| 36 |
+
|
| 37 |
return render(request, 'social/post_list.html', context)
|
| 38 |
|
| 39 |
class PostDetailView(LoginRequiredMixin, View):
|
| 40 |
def get(self, request, pk, *args, **kwargs):
|
| 41 |
post = Post.objects.get(pk=pk)
|
| 42 |
form = CommentForm()
|
| 43 |
+
|
| 44 |
comments = Comment.objects.filter(post=post).order_by('-created_on')
|
| 45 |
|
| 46 |
context = {
|
|
|
|
| 50 |
}
|
| 51 |
|
| 52 |
return render(request, 'social/post_detail.html', context)
|
|
|
|
| 53 |
def post(self, request, pk, *args, **kwargs):
|
| 54 |
post = Post.objects.get(pk=pk)
|
| 55 |
form = CommentForm(request.POST)
|
|
|
|
| 59 |
new_comment.author = request.user
|
| 60 |
new_comment.post = post
|
| 61 |
new_comment.save()
|
| 62 |
+
|
| 63 |
comments = Comment.objects.filter(post=post).order_by('-created_on')
|
| 64 |
|
| 65 |
context = {
|
|
|
|
| 74 |
model = Post
|
| 75 |
fields = ['body']
|
| 76 |
template_name = 'social/post_edit.html'
|
| 77 |
+
|
| 78 |
def get_success_url(self):
|
| 79 |
pk = self.kwargs['pk']
|
| 80 |
return reverse_lazy('post-detail', kwargs={'pk': pk})
|
| 81 |
+
|
| 82 |
def test_func(self):
|
| 83 |
post = self.get_object()
|
| 84 |
return self.request.user == post.author
|
|
|
|
| 101 |
return reverse_lazy('post-detail', kwargs={'pk': pk})
|
| 102 |
|
| 103 |
def test_func(self):
|
| 104 |
+
post = self.get_object()
|
| 105 |
+
return self.request.user == post.author
|
| 106 |
+
|
| 107 |
+
class ProfileView(View):
|
| 108 |
+
def get(self, request, pk, *args, **kwargs):
|
| 109 |
+
profile = UserProfile.objects.get(pk=pk)
|
| 110 |
+
user = profile.user
|
| 111 |
+
posts = Post.objects.filter(author=user).order_by('-created_on')
|
| 112 |
+
|
| 113 |
+
followers = profile.followers.all()
|
| 114 |
+
|
| 115 |
+
if len(followers) == 0:
|
| 116 |
+
is_following = False
|
| 117 |
+
|
| 118 |
+
for follower in followers:
|
| 119 |
+
if follower == request.user:
|
| 120 |
+
is_following = True
|
| 121 |
+
break
|
| 122 |
+
else:
|
| 123 |
+
is_following = False
|
| 124 |
+
|
| 125 |
+
number_of_followers = len(followers)
|
| 126 |
+
|
| 127 |
+
context = {
|
| 128 |
+
'user': user,
|
| 129 |
+
'profile': profile,
|
| 130 |
+
'posts': posts,
|
| 131 |
+
'number_of_followers': number_of_followers,
|
| 132 |
+
'is_following': is_following,
|
| 133 |
+
}
|
| 134 |
+
|
| 135 |
+
return render(request, 'social/profile.html', context)
|
| 136 |
+
|
| 137 |
+
class ProfileEditView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
|
| 138 |
+
model = UserProfile
|
| 139 |
+
fields = ['name', 'bio', 'birth_date', 'location', 'picture']
|
| 140 |
+
template_name = 'social/profile_edit.html'
|
| 141 |
+
|
| 142 |
+
def get_success_url(self):
|
| 143 |
+
pk = self.kwargs['pk']
|
| 144 |
+
return reverse_lazy('profile', kwargs={'pk': pk})
|
| 145 |
+
|
| 146 |
+
def test_func(self):
|
| 147 |
+
profile = self.get_object()
|
| 148 |
+
return self.request.user == profile.user
|
| 149 |
+
|
| 150 |
+
class AddFollower(LoginRequiredMixin, View):
|
| 151 |
+
def post(self, request, pk, *args, **kwargs):
|
| 152 |
+
profile = UserProfile.objects.get(pk=pk)
|
| 153 |
+
profile.followers.add(request.user)
|
| 154 |
+
|
| 155 |
+
return redirect('profile', pk=profile.pk)
|
| 156 |
+
|
| 157 |
+
class RemoveFollower(LoginRequiredMixin, View):
|
| 158 |
+
def post(self, request, pk, *args, **kwargs):
|
| 159 |
+
profile = UserProfile.objects.get(pk=pk)
|
| 160 |
+
profile.followers.remove(request.user)
|
| 161 |
+
|
| 162 |
+
return redirect('profile', pk=profile.pk)
|
| 163 |
+
|
| 164 |
+
class AddLike(LoginRequiredMixin, View):
|
| 165 |
+
def post(self, request, pk, *args, **kwargs):
|
| 166 |
+
post = Post.objects.get(pk=pk)
|
| 167 |
+
|
| 168 |
+
is_dislike = False
|
| 169 |
+
|
| 170 |
+
for dislike in post.dislikes.all():
|
| 171 |
+
if dislike == request.user:
|
| 172 |
+
is_dislike = True
|
| 173 |
+
break
|
| 174 |
+
|
| 175 |
+
if is_dislike:
|
| 176 |
+
post.dislikes.remove(request.user)
|
| 177 |
+
|
| 178 |
+
is_like = False
|
| 179 |
+
|
| 180 |
+
for like in post.likes.all():
|
| 181 |
+
if like == request.user:
|
| 182 |
+
is_like = True
|
| 183 |
+
break
|
| 184 |
+
|
| 185 |
+
if not is_like:
|
| 186 |
+
post.likes.add(request.user)
|
| 187 |
+
|
| 188 |
+
if is_like:
|
| 189 |
+
post.likes.remove(request.user)
|
| 190 |
+
|
| 191 |
+
next = request.POST.get('next', '/')
|
| 192 |
+
return HttpResponseRedirect(next)
|
| 193 |
+
|
| 194 |
+
class AddDislike(LoginRequiredMixin, View):
|
| 195 |
+
def post(self, request, pk, *args, **kwargs):
|
| 196 |
+
post = Post.objects.get(pk=pk)
|
| 197 |
+
|
| 198 |
+
is_like = False
|
| 199 |
+
|
| 200 |
+
for like in post.likes.all():
|
| 201 |
+
if like == request.user:
|
| 202 |
+
is_like = True
|
| 203 |
+
break
|
| 204 |
+
|
| 205 |
+
if is_like:
|
| 206 |
+
post.likes.remove(request.user)
|
| 207 |
+
|
| 208 |
+
is_dislike = False
|
| 209 |
+
|
| 210 |
+
for dislike in post.dislikes.all():
|
| 211 |
+
if dislike == request.user:
|
| 212 |
+
is_dislike = True
|
| 213 |
+
break
|
| 214 |
+
|
| 215 |
+
if not is_dislike:
|
| 216 |
+
post.dislikes.add(request.user)
|
| 217 |
+
|
| 218 |
+
if is_dislike:
|
| 219 |
+
post.dislikes.remove(request.user)
|
| 220 |
+
|
| 221 |
+
next = request.POST.get('next', '/')
|
| 222 |
+
return HttpResponseRedirect(next)
|