Initial profile management page
This commit is contained in:
parent
2cef5bf87a
commit
9abd81245e
|
@ -0,0 +1,41 @@
|
||||||
|
pipeline:
|
||||||
|
backend:
|
||||||
|
commands:
|
||||||
|
- python manage.py test
|
||||||
|
|
||||||
|
#volumes:
|
||||||
|
# postgres_data_dev: {}
|
||||||
|
# postgres_backup_dev: {}
|
||||||
|
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
build: ./compose/postgres
|
||||||
|
# volumes:
|
||||||
|
# - postgres_data_dev:/var/lib/postgresql/data
|
||||||
|
# - postgres_backup_dev:/backups
|
||||||
|
environment:
|
||||||
|
- POSTGRES_USER=mhackspace
|
||||||
|
|
||||||
|
# django:
|
||||||
|
# build:
|
||||||
|
# context: .
|
||||||
|
# dockerfile: ./compose/django/Dockerfile-dev
|
||||||
|
# command: /start-dev.sh
|
||||||
|
# depends_on:
|
||||||
|
# - postgres
|
||||||
|
# environment:
|
||||||
|
# - POSTGRES_USER=mhackspace
|
||||||
|
# - USE_DOCKER=yes
|
||||||
|
# volumes:
|
||||||
|
# - .:/app
|
||||||
|
# ports:
|
||||||
|
# - "8180:8000"
|
||||||
|
# links:
|
||||||
|
# - postgres
|
||||||
|
# - mailhog
|
||||||
|
|
||||||
|
mailhog:
|
||||||
|
image: mailhog/mailhog
|
||||||
|
ports:
|
||||||
|
- "8125:8025"
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
media/*
|
|
@ -40,3 +40,11 @@ docker-compose -f dev.yml run django python manage.py migrate
|
||||||
#+BEGIN_SRC sh
|
#+BEGIN_SRC sh
|
||||||
docker-compose -f dev.yml run django python manage.py createsuperuser
|
docker-compose -f dev.yml run django python manage.py createsuperuser
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
|
|
||||||
|
** Migrations / Managing default data
|
||||||
|
If you want to export some data you entered into the admin area you can use =dumpdata= and =loaddata= to export and import.
|
||||||
|
|
||||||
|
#+BEGIN_SRC sh
|
||||||
|
docker-compose -fdev.yml run django python manage.py dumpdata feeds > mhackspace/feeds/fixtures/default.json
|
||||||
|
#+END_SRC
|
||||||
|
|
|
@ -45,10 +45,10 @@ THIRD_PARTY_APPS = (
|
||||||
# Apps specific for this project go here.
|
# Apps specific for this project go here.
|
||||||
LOCAL_APPS = (
|
LOCAL_APPS = (
|
||||||
# custom users app
|
# custom users app
|
||||||
|
# Your stuff: custom apps go here
|
||||||
'mhackspace.users.apps.UsersConfig',
|
'mhackspace.users.apps.UsersConfig',
|
||||||
'mhackspace.feeds',
|
'mhackspace.feeds',
|
||||||
'mhackspace.contact',
|
'mhackspace.contact',
|
||||||
# Your stuff: custom apps go here
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# See: https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
|
# See: https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
|
||||||
|
|
|
@ -17,6 +17,7 @@ urlpatterns = [
|
||||||
|
|
||||||
url(r'^contact/$', contact, name='contact'),
|
url(r'^contact/$', contact, name='contact'),
|
||||||
|
|
||||||
|
# need to be logged in for these urls
|
||||||
# Django Admin, use {% url 'admin:index' %}
|
# Django Admin, use {% url 'admin:index' %}
|
||||||
url(settings.ADMIN_URL, admin.site.urls),
|
url(settings.ADMIN_URL, admin.site.urls),
|
||||||
|
|
||||||
|
|
2
dev.yml
2
dev.yml
|
@ -34,5 +34,5 @@ services:
|
||||||
mailhog:
|
mailhog:
|
||||||
image: mailhog/mailhog
|
image: mailhog/mailhog
|
||||||
ports:
|
ports:
|
||||||
- "8025:8125"
|
- "8125:8025"
|
||||||
|
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
|
@ -28,6 +28,9 @@
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<div class="m-b-1">
|
<div class="m-b-1">
|
||||||
|
<a id="mini_logo" href="/login">
|
||||||
|
<img src="/static/images/hackspace.png" class="mini-logo">
|
||||||
|
</a>
|
||||||
<nav class="navbar navbar-dark navbar-static-top bg-inverse">
|
<nav class="navbar navbar-dark navbar-static-top bg-inverse">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<a class="navbar-brand" href="/">Maidstone Hackspace</a>
|
<a class="navbar-brand" href="/">Maidstone Hackspace</a>
|
||||||
|
|
|
@ -6,6 +6,32 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
||||||
|
<h2>Profile</h2>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<img src="{{ MEDIA_URL }}{{ user.image }}" alt="Profile image"/>
|
||||||
|
|
||||||
|
<p>{{ user.username }}</p>
|
||||||
|
<p>{{ user.name }}</p>
|
||||||
|
<p>{{ user.email }}</p>
|
||||||
|
<p>Last login {{ user.last_login}}</p>
|
||||||
|
<p>Member since</p>
|
||||||
|
<p>Description: {{ blurb.description }}</p>
|
||||||
|
<p>Skills: {{ blurb.description }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div id="membercard" class="registered">
|
||||||
|
<div class="date">Joined </div>
|
||||||
|
<div class="container">
|
||||||
|
<div class="middle">
|
||||||
|
<p>MHS{{ user.id|stringformat:"05d" }}</p><p>Change me</p>
|
||||||
|
<a href="/profile/membership/cancel">Cancel Membership</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,10 @@
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>{{ user.username }}</h1>
|
<h1>{{ user.username }}</h1>
|
||||||
<form class="form-horizontal" method="post" action="{% url 'users:update' %}">
|
<form class="form-horizontal" method="post" action="{% url 'users:update' %}" enctype="multipart/form-data">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form|crispy }}
|
{{ form|crispy }}
|
||||||
|
{{ form_blurb|crispy }}
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<button type="submit" class="btn">Update</button>
|
<button type="submit" class="btn">Update</button>
|
||||||
|
|
|
@ -36,7 +36,7 @@ class MyUserAdmin(AuthUserAdmin):
|
||||||
form = MyUserChangeForm
|
form = MyUserChangeForm
|
||||||
add_form = MyUserCreationForm
|
add_form = MyUserCreationForm
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
('User Profile', {'fields': ('name',)}),
|
('User Profile', {'fields': ('name', 'image')}),
|
||||||
) + AuthUserAdmin.fieldsets
|
) + AuthUserAdmin.fieldsets
|
||||||
list_display = ('username', 'name', 'is_superuser')
|
list_display = ('username', 'name', 'is_superuser')
|
||||||
search_fields = ['name']
|
search_fields = ['name']
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from django.db import models
|
||||||
|
from django.forms import ModelForm
|
||||||
|
|
||||||
|
from .models import UserBlurb
|
||||||
|
|
||||||
|
class UserBlurbForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = UserBlurb
|
||||||
|
exclude = ['user']
|
|
@ -0,0 +1,36 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.4 on 2017-01-06 08:53
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Membership',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('payment', models.DecimalField(decimal_places=2, max_digits=6)),
|
||||||
|
('date', models.DateTimeField()),
|
||||||
|
('reference', models.CharField(max_length=255)),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='UserBlurb',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('skills', models.CharField(max_length=255)),
|
||||||
|
('description', models.TextField()),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,20 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.4 on 2017-01-06 19:03
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0002_membership_userblurb'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='user',
|
||||||
|
name='image',
|
||||||
|
field=models.ImageField(blank=True, null=True, upload_to='/upload/avatars'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,24 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.4 on 2017-01-06 20:30
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0003_user_image'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameModel(
|
||||||
|
old_name='Membership',
|
||||||
|
new_name='UserMembership',
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='user',
|
||||||
|
name='image',
|
||||||
|
field=models.ImageField(blank=True, null=True, upload_to='avatars/'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals, absolute_import
|
from __future__ import unicode_literals, absolute_import
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import AbstractUser
|
from django.contrib.auth.models import AbstractUser
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
@ -10,13 +11,24 @@ from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class User(AbstractUser):
|
class User(AbstractUser):
|
||||||
|
|
||||||
# First Name and Last Name do not cover name patterns
|
|
||||||
# around the globe.
|
|
||||||
name = models.CharField(_('Name of User'), blank=True, max_length=255)
|
name = models.CharField(_('Name of User'), blank=True, max_length=255)
|
||||||
|
image = models.ImageField(upload_to='avatars/', blank=True, null=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.username
|
return self.username
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('users:detail', kwargs={'username': self.username})
|
return reverse('users:detail', kwargs={'username': self.username})
|
||||||
|
|
||||||
|
|
||||||
|
class UserBlurb(models.Model):
|
||||||
|
user = models.ForeignKey(settings.AUTH_USER_MODEL)
|
||||||
|
skills = models.CharField(max_length=255)
|
||||||
|
description = models.TextField()
|
||||||
|
|
||||||
|
|
||||||
|
class UserMembership(models.Model):
|
||||||
|
user = models.ForeignKey(settings.AUTH_USER_MODEL)
|
||||||
|
payment = models.DecimalField(max_digits=6, decimal_places=2)
|
||||||
|
date = models.DateTimeField()
|
||||||
|
reference = models.CharField(max_length=255)
|
||||||
|
|
|
@ -7,7 +7,10 @@ from django.views.generic import DetailView, ListView, RedirectView, UpdateView
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
|
||||||
from .models import User
|
from .models import User
|
||||||
|
from .models import UserBlurb
|
||||||
|
from .models import UserMembership
|
||||||
|
|
||||||
|
from .forms import UserBlurbForm
|
||||||
|
|
||||||
class UserDetailView(LoginRequiredMixin, DetailView):
|
class UserDetailView(LoginRequiredMixin, DetailView):
|
||||||
model = User
|
model = User
|
||||||
|
@ -15,6 +18,12 @@ class UserDetailView(LoginRequiredMixin, DetailView):
|
||||||
slug_field = 'username'
|
slug_field = 'username'
|
||||||
slug_url_kwarg = 'username'
|
slug_url_kwarg = 'username'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
# xxx will be available in the template as the related objects
|
||||||
|
context = super(UserDetailView, self).get_context_data(**kwargs)
|
||||||
|
context['blurb'] = UserBlurb.objects.filter(user=self.get_object()).first()
|
||||||
|
context['membership'] = UserMembership.objects.filter(user=self.get_object()).first()
|
||||||
|
return context
|
||||||
|
|
||||||
class UserRedirectView(LoginRequiredMixin, RedirectView):
|
class UserRedirectView(LoginRequiredMixin, RedirectView):
|
||||||
permanent = False
|
permanent = False
|
||||||
|
@ -25,21 +34,34 @@ class UserRedirectView(LoginRequiredMixin, RedirectView):
|
||||||
|
|
||||||
|
|
||||||
class UserUpdateView(LoginRequiredMixin, UpdateView):
|
class UserUpdateView(LoginRequiredMixin, UpdateView):
|
||||||
|
fields = ['name', 'image', ]
|
||||||
fields = ['name', ]
|
|
||||||
|
|
||||||
# we already imported User in the view code above, remember?
|
|
||||||
model = User
|
model = User
|
||||||
|
|
||||||
# send the user back to their own page after a successful update
|
# send the user back to their own page after a successful update
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse('users:detail',
|
return reverse(
|
||||||
|
'users:detail',
|
||||||
kwargs={'username': self.request.user.username})
|
kwargs={'username': self.request.user.username})
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(UserUpdateView, self).get_context_data(**kwargs)
|
||||||
|
profile = UserBlurb.objects.filter(user=self.get_object()).first()
|
||||||
|
context['form_blurb'] = UserBlurbForm(instance=profile)
|
||||||
|
return context
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
# Only get the User record for the user making the request
|
# Only get the User record for the user making the request
|
||||||
return User.objects.get(username=self.request.user.username)
|
return User.objects.get(username=self.request.user.username)
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
profile = UserBlurb.objects.filter(user=self.get_object()).first()
|
||||||
|
form_blurb = UserBlurbForm(self.request.POST, instance=profile)
|
||||||
|
if form_blurb.is_valid():
|
||||||
|
blurb_model = form_blurb.save(commit=False)
|
||||||
|
blurb_model.user = self.request.user
|
||||||
|
blurb_model.save()
|
||||||
|
|
||||||
|
return super(UserUpdateView, self).form_valid(form)
|
||||||
|
|
||||||
class UserListView(LoginRequiredMixin, ListView):
|
class UserListView(LoginRequiredMixin, ListView):
|
||||||
model = User
|
model = User
|
||||||
|
|
Loading…
Reference in New Issue