diff --git a/chirper/blog/migrations/0003_remove_post_title.py b/chirper/blog/migrations/0003_remove_post_title.py
new file mode 100644
index 0000000..a48f622
--- /dev/null
+++ b/chirper/blog/migrations/0003_remove_post_title.py
@@ -0,0 +1,17 @@
+# Generated by Django 3.0.6 on 2020-05-26 03:31
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('blog', '0002_auto_20200430_0415'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='post',
+ name='title',
+ ),
+ ]
diff --git a/chirper/blog/models.py b/chirper/blog/models.py
index a2f5786..897e04a 100644
--- a/chirper/blog/models.py
+++ b/chirper/blog/models.py
@@ -1,15 +1,18 @@
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
+from django.urls import reverse
# Create your models here.
class Post(models.Model):
- title = models.CharField(max_length=100)
content = models.TextField()
date_posted = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
likes = models.IntegerField(default=0)
def __str__(self):
- return self.title
+ return f"{self.author}'s post on {self.date_posted}"
+
+ def get_absolute_url(self):
+ return reverse('post-detail', kwargs={'pk': self.pk})
diff --git a/chirper/blog/static/Segoe UI Bold Italic.ttfZone.Identifier b/chirper/blog/static/Segoe UI Bold Italic.ttfZone.Identifier
new file mode 100644
index 0000000..37425d8
--- /dev/null
+++ b/chirper/blog/static/Segoe UI Bold Italic.ttfZone.Identifier
@@ -0,0 +1,3 @@
+[ZoneTransfer]
+ZoneId=3
+ReferrerUrl=C:\Users\rushi\Downloads\segoe-ui-4-cufonfonts.zip
diff --git a/chirper/blog/static/Segoe UI Bold.ttfZone.Identifier b/chirper/blog/static/Segoe UI Bold.ttfZone.Identifier
new file mode 100644
index 0000000..37425d8
--- /dev/null
+++ b/chirper/blog/static/Segoe UI Bold.ttfZone.Identifier
@@ -0,0 +1,3 @@
+[ZoneTransfer]
+ZoneId=3
+ReferrerUrl=C:\Users\rushi\Downloads\segoe-ui-4-cufonfonts.zip
diff --git a/chirper/blog/static/Segoe UI Italic.ttfZone.Identifier b/chirper/blog/static/Segoe UI Italic.ttfZone.Identifier
new file mode 100644
index 0000000..37425d8
--- /dev/null
+++ b/chirper/blog/static/Segoe UI Italic.ttfZone.Identifier
@@ -0,0 +1,3 @@
+[ZoneTransfer]
+ZoneId=3
+ReferrerUrl=C:\Users\rushi\Downloads\segoe-ui-4-cufonfonts.zip
diff --git a/chirper/blog/static/Segoe UI.ttfZone.Identifier b/chirper/blog/static/Segoe UI.ttfZone.Identifier
new file mode 100644
index 0000000..37425d8
--- /dev/null
+++ b/chirper/blog/static/Segoe UI.ttfZone.Identifier
@@ -0,0 +1,3 @@
+[ZoneTransfer]
+ZoneId=3
+ReferrerUrl=C:\Users\rushi\Downloads\segoe-ui-4-cufonfonts.zip
diff --git a/chirper/blog/static/blog/Segoe UI Bold Italic.ttf b/chirper/blog/static/blog/Segoe UI Bold Italic.ttf
new file mode 100644
index 0000000..d134de8
Binary files /dev/null and b/chirper/blog/static/blog/Segoe UI Bold Italic.ttf differ
diff --git a/chirper/blog/static/blog/Segoe UI Bold.ttf b/chirper/blog/static/blog/Segoe UI Bold.ttf
new file mode 100644
index 0000000..5723e8b
Binary files /dev/null and b/chirper/blog/static/blog/Segoe UI Bold.ttf differ
diff --git a/chirper/blog/static/blog/Segoe UI Italic.ttf b/chirper/blog/static/blog/Segoe UI Italic.ttf
new file mode 100644
index 0000000..07fdf85
Binary files /dev/null and b/chirper/blog/static/blog/Segoe UI Italic.ttf differ
diff --git a/chirper/blog/static/blog/Segoe UI.ttf b/chirper/blog/static/blog/Segoe UI.ttf
new file mode 100644
index 0000000..46b3b99
Binary files /dev/null and b/chirper/blog/static/blog/Segoe UI.ttf differ
diff --git a/chirper/blog/static/blog/styles.css b/chirper/blog/static/blog/styles.css
index 1ae2a57..363abcc 100644
--- a/chirper/blog/static/blog/styles.css
+++ b/chirper/blog/static/blog/styles.css
@@ -3,11 +3,16 @@
src: url(futura.ttf);
}
+@font-face {
+ font-family: 'Segoe UI';
+ src: url('Segoe UI.ttf');
+}
+
body {
background: #fafafa;
color: #333333;
margin-top: 5rem;
- font-family: Futura;
+ font-family: 'Segoe UI';
}
h1, h2, h3, h4, h5, h6 {
@@ -21,7 +26,7 @@ ul {
.bg-steel {
background: #5f788a;
background: linear-gradient(to right, #ec008c, #fc6767);
- border-bottom: 3px #111 solid;
+ /* border-bottom: 3px #111 solid; */
}
.site-header .navbar-nav .nav-link {
@@ -90,3 +95,8 @@ a.article-title:hover {
.account-heading {
font-size: 2.5rem;
}
+
+.nounderline {
+ text-decoration: none !important;
+ color: black !important;
+}
diff --git a/chirper/blog/templates/blog/base.html b/chirper/blog/templates/blog/base.html
index 9d75617..3067fbb 100644
--- a/chirper/blog/templates/blog/base.html
+++ b/chirper/blog/templates/blog/base.html
@@ -35,7 +35,13 @@
@@ -57,6 +63,19 @@
{% endif %}
{% block content %}{% endblock %}
+
+
diff --git a/chirper/blog/templates/blog/home.html b/chirper/blog/templates/blog/home.html
index 4fb5a32..bc19523 100644
--- a/chirper/blog/templates/blog/home.html
+++ b/chirper/blog/templates/blog/home.html
@@ -2,14 +2,35 @@
{% block content %}
{% for post in posts %}
+
{% endfor %}
+ {% if is_paginated %}
+
+ {% if page_obj.has_previous %}
+ First
+ Previous
+ {% endif %}
+
+ {% for num in page_obj.paginator.page_range %}
+ {% if page_obj.number == num %}
+ {{ num }}
+ {% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
+ {{ num }}
+ {% endif %}
+ {% endfor %}
+
+ {% if page_obj.has_next %}
+ Next
+ Last
+ {% endif %}
+
+ {% endif %}
{% endblock content %}
diff --git a/chirper/blog/templates/blog/post_confirm_delete.html b/chirper/blog/templates/blog/post_confirm_delete.html
new file mode 100644
index 0000000..d175393
--- /dev/null
+++ b/chirper/blog/templates/blog/post_confirm_delete.html
@@ -0,0 +1,27 @@
+{% extends "blog/base.html" %}
+{% block content %}
+
+
+{% endblock content %}
diff --git a/chirper/blog/templates/blog/post_detail.html b/chirper/blog/templates/blog/post_detail.html
new file mode 100644
index 0000000..e6de4cc
--- /dev/null
+++ b/chirper/blog/templates/blog/post_detail.html
@@ -0,0 +1,19 @@
+{% extends "blog/base.html" %}
+{% block content %}
+
+
+
+
+{% endblock content %}
diff --git a/chirper/blog/templates/blog/post_form.html b/chirper/blog/templates/blog/post_form.html
new file mode 100644
index 0000000..7a07423
--- /dev/null
+++ b/chirper/blog/templates/blog/post_form.html
@@ -0,0 +1,16 @@
+{% extends "blog/base.html" %}
+{% load crispy_forms_tags %}
+{% block content %}
+
+
+{% endblock content %}
diff --git a/chirper/blog/templates/blog/user_posts.html b/chirper/blog/templates/blog/user_posts.html
new file mode 100644
index 0000000..bf349cc
--- /dev/null
+++ b/chirper/blog/templates/blog/user_posts.html
@@ -0,0 +1,37 @@
+{% extends "blog/base.html" %}
+{% block content %}
+ Posts by {{ view.kwargs.username }} ({{ page_obj.paginator.count }})
+ {% for post in posts %}
+
+
+
+
+ {% endfor %}
+ {% if is_paginated %}
+
+ {% if page_obj.has_previous %}
+ First
+ Previous
+ {% endif %}
+
+ {% for num in page_obj.paginator.page_range %}
+ {% if page_obj.number == num %}
+ {{ num }}
+ {% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
+ {{ num }}
+ {% endif %}
+ {% endfor %}
+
+ {% if page_obj.has_next %}
+ Next
+ Last
+ {% endif %}
+
+ {% endif %}
+{% endblock content %}
diff --git a/chirper/blog/urls.py b/chirper/blog/urls.py
index 2f2d169..205972c 100644
--- a/chirper/blog/urls.py
+++ b/chirper/blog/urls.py
@@ -1,7 +1,23 @@
from django.urls import path
+from .views import (
+ PostListView,
+ PostDetailView,
+ PostCreateView,
+ PostUpdateView,
+ PostDeleteView,
+ UserPostListView,
+)
from . import views
+
urlpatterns = [
- path('', views.home, name='blog-home'),
+ path('', PostListView.as_view(), name='blog-home'),
+
+ path('user/', UserPostListView.as_view(), name='user-posts'),
+ path('post//', PostDetailView.as_view(), name='post-detail'),
+ path('post/new/', PostCreateView.as_view(), name='post-create'),
+ path('post//update/', PostUpdateView.as_view(), name='post-update'),
+ path('post//delete/', PostDeleteView.as_view(), name='post-delete'),
+
path('about/', views.about, name='blog-about'),
]
diff --git a/chirper/blog/views.py b/chirper/blog/views.py
index 1067259..82f6ecc 100644
--- a/chirper/blog/views.py
+++ b/chirper/blog/views.py
@@ -1,4 +1,13 @@
-from django.shortcuts import render
+from django.shortcuts import render, get_object_or_404
+from django.contrib.auth.models import User
+from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
+from django.views.generic import (
+ ListView,
+ DetailView,
+ CreateView,
+ UpdateView,
+ DeleteView,
+)
from .models import Post
def home (request):
@@ -7,5 +16,53 @@ def home (request):
}
return render(request, 'blog/home.html', context)
+class PostListView(ListView):
+ model = Post
+ template_name = "blog/home.html"
+ context_object_name='posts'
+ ordering = ['-date_posted']
+ paginate_by = 8
+
+class UserPostListView(ListView):
+ model = Post
+ template_name = "blog/user_posts.html"
+ context_object_name='posts'
+ paginate_by = 8
+
+ def get_queryset(self):
+ user = get_object_or_404(User, username=self.kwargs.get('username'))
+ return Post.objects.filter(author=user).order_by('-date_posted')
+
+class PostDetailView(DetailView):
+ model = Post
+
+class PostCreateView(LoginRequiredMixin, CreateView):
+ model = Post
+ fields=['content']
+
+ def form_valid(self, form):
+ form.instance.author = self.request.user
+ return super().form_valid(form)
+
+class PostUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
+ model = Post
+ fields=['content']
+
+ def form_valid(self, form):
+ form.instance.author = self.request.user
+ return super().form_valid(form)
+
+ def test_func(self):
+ post = self.get_object()
+ return self.request.user == post.author
+
+class PostDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
+ model = Post
+ success_url='/'
+
+ def test_func(self):
+ post = self.get_object()
+ return self.request.user == post.author
+
def about (request):
return render(request, 'blog/about.html', {'title': 'About'})
diff --git a/chirper/chirper/settings.py b/chirper/chirper/settings.py
index 213e042..c9efa9f 100644
--- a/chirper/chirper/settings.py
+++ b/chirper/chirper/settings.py
@@ -46,6 +46,7 @@ INSTALLED_APPS = [
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
+ 'crispy_forms',
'oauth2_provider',
'corsheaders',
]
@@ -131,3 +132,11 @@ USE_TZ = True
# https://docs.djangoproject.com/en/3.0/howto/static-files/
STATIC_URL = '/static/'
+
+CRISPY_TEMPLATE_PACK = 'bootstrap4'
+
+MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
+MEDIA_URL = '/media/'
+
+LOGIN_REDIRECT_URL = 'blog-home'
+LOGIN_URL = 'login'
diff --git a/chirper/chirper/urls.py b/chirper/chirper/urls.py
index 1d5cec3..1166080 100644
--- a/chirper/chirper/urls.py
+++ b/chirper/chirper/urls.py
@@ -13,6 +13,8 @@ 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.conf import settings
+from django.conf.urls.static import static
from django.contrib import admin
from django.urls import path, include
from users import views as user_views
@@ -20,6 +22,12 @@ from users import views as user_views
urlpatterns = [
path('', include('blog.urls')),
path('admin/', admin.site.urls),
- path('login/', include('users.urls')),
+ path('login/', user_views.login, name='login'),
+ path('logout/', user_views.logout, name='logout'),
+ path('callback/', user_views.callback),
+ path('profile/', user_views.profile, name='profile'),
path("o/", include('oauth2_provider.urls', namespace='oauth2_provider')),
]
+
+if settings.DEBUG:
+ urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
diff --git a/chirper/manage.py b/chirper/manage.py
old mode 100755
new mode 100644
diff --git a/chirper/media/default.jpg b/chirper/media/default.jpg
new file mode 100644
index 0000000..ee224a6
Binary files /dev/null and b/chirper/media/default.jpg differ
diff --git a/chirper/media/default2.jpg b/chirper/media/default2.jpg
new file mode 100644
index 0000000..01968b0
Binary files /dev/null and b/chirper/media/default2.jpg differ
diff --git a/chirper/media/profile_pics/pfp.jpg b/chirper/media/profile_pics/pfp.jpg
new file mode 100644
index 0000000..160d279
Binary files /dev/null and b/chirper/media/profile_pics/pfp.jpg differ
diff --git a/chirper/media/profile_pics/pfp_42i5z4n.jpg b/chirper/media/profile_pics/pfp_42i5z4n.jpg
new file mode 100644
index 0000000..160d279
Binary files /dev/null and b/chirper/media/profile_pics/pfp_42i5z4n.jpg differ
diff --git a/chirper/media/profile_pics/pfp_cnFx55X.jpg b/chirper/media/profile_pics/pfp_cnFx55X.jpg
new file mode 100644
index 0000000..8b3fde7
Binary files /dev/null and b/chirper/media/profile_pics/pfp_cnFx55X.jpg differ
diff --git a/chirper/media/profile_pics/pfp_wMzpNdz.jpg b/chirper/media/profile_pics/pfp_wMzpNdz.jpg
new file mode 100644
index 0000000..160d279
Binary files /dev/null and b/chirper/media/profile_pics/pfp_wMzpNdz.jpg differ
diff --git a/chirper/users/admin.py b/chirper/users/admin.py
index 8c38f3f..123dd95 100644
--- a/chirper/users/admin.py
+++ b/chirper/users/admin.py
@@ -1,3 +1,5 @@
from django.contrib import admin
+from .models import Profile
# Register your models here.
+admin.site.register(Profile)
diff --git a/chirper/users/apps.py b/chirper/users/apps.py
index 4ce1fab..b8d67f1 100644
--- a/chirper/users/apps.py
+++ b/chirper/users/apps.py
@@ -3,3 +3,6 @@ from django.apps import AppConfig
class UsersConfig(AppConfig):
name = 'users'
+
+ def ready(self):
+ import users.signals
diff --git a/chirper/users/forms.py b/chirper/users/forms.py
new file mode 100644
index 0000000..bba9641
--- /dev/null
+++ b/chirper/users/forms.py
@@ -0,0 +1,22 @@
+from django import forms
+from django.contrib.auth.models import User
+from .models import Profile
+
+class UserUpdateForm(forms.ModelForm):
+
+ email = forms.EmailField()
+
+ def __init__(self, *args, **kwargs):
+ super(UserUpdateForm, self).__init__(*args, **kwargs)
+ instance = getattr(self, 'instance', None)
+ if instance and instance.pk:
+ self.fields['username'].widget.attrs['readonly'] = True
+
+ class Meta:
+ model = User
+ fields = ['username', 'email']
+
+class ProfileUpdateForm(forms.ModelForm):
+ class Meta:
+ model = Profile
+ fields = ['profile_pic']
diff --git a/chirper/users/migrations/0001_initial.py b/chirper/users/migrations/0001_initial.py
new file mode 100644
index 0000000..7cd90b8
--- /dev/null
+++ b/chirper/users/migrations/0001_initial.py
@@ -0,0 +1,25 @@
+# Generated by Django 3.0.6 on 2020-05-26 03:31
+
+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='Profile',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('profile_pic', models.ImageField(default='default.jpg', upload_to='profile.pics')),
+ ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+ ],
+ ),
+ ]
diff --git a/chirper/users/migrations/0002_auto_20200526_0344.py b/chirper/users/migrations/0002_auto_20200526_0344.py
new file mode 100644
index 0000000..ab57398
--- /dev/null
+++ b/chirper/users/migrations/0002_auto_20200526_0344.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.0.6 on 2020-05-26 03:44
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('users', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='profile',
+ name='profile_pic',
+ field=models.ImageField(default='default.jpg', upload_to='profile_pics'),
+ ),
+ ]
diff --git a/chirper/users/models.py b/chirper/users/models.py
index 71a8362..0fe5e30 100644
--- a/chirper/users/models.py
+++ b/chirper/users/models.py
@@ -1,3 +1,22 @@
from django.db import models
+from django.contrib.auth.models import User
+from PIL import Image
# Create your models here.
+
+class Profile(models.Model):
+ user = models.OneToOneField(User, on_delete=models.CASCADE)
+ profile_pic = models.ImageField(default='default.jpg', upload_to='profile_pics')
+
+ def __str__ (self):
+ return f"{self.user.username}'s Profile"
+
+ def save(self):
+ super().save()
+
+ img = Image.open(self.profile_pic.path)
+
+ if img.height > 300 or img.width > 300:
+ size = (300, 300)
+ img.thumbnail(size)
+ img.save(self.profile_pic.path)
diff --git a/chirper/users/signals.py b/chirper/users/signals.py
new file mode 100644
index 0000000..5be6310
--- /dev/null
+++ b/chirper/users/signals.py
@@ -0,0 +1,13 @@
+from django.db.models.signals import post_save
+from django.contrib.auth.models import User
+from django.dispatch import receiver
+from .models import Profile
+
+@receiver(post_save, sender=User)
+def create_profile(sender, instance, created, **kwargs):
+ if created:
+ Profile.objects.create(user=instance)
+
+@receiver(post_save, sender=User)
+def save_profile(sender, instance, **kwargs):
+ instance.profile.save()
diff --git a/chirper/users/templates/users/login.html b/chirper/users/templates/users/login.html
index 50eecdd..04e3041 100644
--- a/chirper/users/templates/users/login.html
+++ b/chirper/users/templates/users/login.html
@@ -1,6 +1,9 @@
{% extends "blog/base.html" %}
{% block content %}
-
+
{% endblock content %}
diff --git a/chirper/users/templates/users/logout.html b/chirper/users/templates/users/logout.html
new file mode 100644
index 0000000..8a4e050
--- /dev/null
+++ b/chirper/users/templates/users/logout.html
@@ -0,0 +1,9 @@
+{% extends "blog/base.html" %}
+{% block content %}
+ You have been logged out
+
+{% endblock content %}
diff --git a/chirper/users/templates/users/profile.html b/chirper/users/templates/users/profile.html
new file mode 100644
index 0000000..3c529bc
--- /dev/null
+++ b/chirper/users/templates/users/profile.html
@@ -0,0 +1,25 @@
+{% extends "blog/base.html" %}
+{% load crispy_forms_tags %}
+{% block content %}
+
+
+{% endblock content %}
diff --git a/chirper/users/urls.py b/chirper/users/urls.py
deleted file mode 100644
index 9082c24..0000000
--- a/chirper/users/urls.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from django.urls import path
-from . import views
-
-urlpatterns = [
- path('', views.login, name='login'),
- path('callback/', views.callback)
-]
diff --git a/chirper/users/views.py b/chirper/users/views.py
index 8929f7e..2089409 100644
--- a/chirper/users/views.py
+++ b/chirper/users/views.py
@@ -1,19 +1,28 @@
-from django.shortcuts import render, redirect
-from django.contrib.auth.models import User
-from django.contrib.auth import authenticate
-from django.contrib.auth import login as auth_login
-from django.contrib import messages
-from requests_oauthlib import OAuth2Session
import json
import requests
+from django.shortcuts import render, redirect
+
+from django.contrib.auth import authenticate
+from django.contrib.auth import login as auth_login
+from django.contrib.auth import logout as auth_logout
+from django.contrib.auth.models import User
+from django.contrib.auth.decorators import login_required
+
+from requests_oauthlib import OAuth2Session
+
+from django.contrib import messages
+
+from .forms import UserUpdateForm, ProfileUpdateForm
+
+
# Create your views here.
client_id = r'6p7HJlFCD8cnBNBEdgMsdULS5ph0jserw1xvWfxX'
client_secret = r'E1e79KebxzAp0LBEtxcUg32b0qFP9Ap9Dxqkac6Qhci5AwXFhSfrbe7MtmGJUh6DDgxivJpGgFYNQgusfvoSraDAnsq3NnEET5DmxgfBBvvuYc2bwDq6KpeKIDQqFtwz'
-redirect_uri = 'http://localhost:8000/login/callback/'
+redirect_uri = 'http://localhost:8000/callback/'
token_url = 'https://ion.tjhsst.edu/oauth/authorize/'
-scope=["read","write"]
+scope=["read"]
authorized_users=["2023rumareti", "Your ION_USERNAME"]
# Hey that's me
@@ -59,11 +68,40 @@ def callback (request):
user = User.objects.create_user(username=username, email=email, password=username, first_name=first_name, last_name=last_name)
user.save()
auth_login(request, user, backend='django.contrib.auth.backends.ModelBackend')
- messages.success(request, f"Welcome to Chirper, {first_name}, we hope you like your stay!")
- return redirect('/')
+ messages.success(request, f"Welcome to Chirper, {first_name}, we hope you enjoy your stay!")
+ return redirect('profile')
else:
messages.error(request, "Sorry, you're not an authorized Ion user!", extra_tags='danger')
- return redirect('/')
+ return redirect('blog-home')
messages.warning(request, "Invalid Callback Response")
- return redirect('/')
+ return redirect('blog-home')
+
+@login_required
+def logout(request):
+ auth_logout(request)
+ return render(request, 'users/logout.html')
+
+@login_required
+def profile(request):
+ if request.method == "POST":
+ userForm = UserUpdateForm(request.POST, instance=request.user)
+ profileForm = ProfileUpdateForm(request.POST,
+ request.FILES,
+ instance=request.user.profile)
+
+ if userForm.is_valid() and profileForm.is_valid():
+ userForm.save()
+ profileForm.save()
+ messages.success(request, "Your account has been updated!")
+ return redirect('profile')
+ else:
+ userForm = UserUpdateForm(instance=request.user)
+ profileForm = ProfileUpdateForm(instance=request.user.profile)
+
+ context = {
+ 'userForm': userForm,
+ 'profileForm': profileForm
+ }
+
+ return render(request, 'users/profile.html', context)