From 2b6bc762e81b07833bd963bb6f45fddf6873c8b0 Mon Sep 17 00:00:00 2001 From: Andrew Buss Date: Fri, 1 May 2015 02:20:39 -0700 Subject: [PATCH] expanded registration --- flashcards/admin.py | 7 +- flashcards/api.py | 8 +- flashcards/migrations/0001_initial.py | 157 ++++++++++++++++++++++++++++++++++ flashcards/migrations/__init__.py | 0 flashcards/models.py | 43 ++-------- flashcards/serializers.py | 4 +- flashy/settings.py | 5 +- 7 files changed, 178 insertions(+), 46 deletions(-) create mode 100644 flashcards/migrations/0001_initial.py create mode 100644 flashcards/migrations/__init__.py diff --git a/flashcards/admin.py b/flashcards/admin.py index 5392cff..943a134 100644 --- a/flashcards/admin.py +++ b/flashcards/admin.py @@ -1,11 +1,14 @@ from django.contrib import admin from flashcards.models import Flashcard, UserFlashcard, Section, FlashcardMask, \ - UserFlashcardReview + UserFlashcardReview, LecturePeriod, User +from simple_email_confirmation import EmailAddress admin.site.register([ + User, Flashcard, FlashcardMask, UserFlashcard, UserFlashcardReview, - Section + Section, + LecturePeriod ]) \ No newline at end of file diff --git a/flashcards/api.py b/flashcards/api.py index d473729..6c887b7 100644 --- a/flashcards/api.py +++ b/flashcards/api.py @@ -1,3 +1,4 @@ +from django.contrib.auth import login, authenticate from django.core.mail import send_mail from rest_framework.views import APIView from rest_framework.response import Response @@ -41,8 +42,8 @@ class UserDetail(APIView): raise ValidationError('Password is required') email = request.data['email'] - user = User.objects.create_user(email) - + user = User.objects.create_user(email, email=email) + user.confirm_email(user.confirmation_key) body = ''' Visit the following link to confirm your email address: http://flashy.cards/app/verify_email/%s @@ -54,6 +55,9 @@ class UserDetail(APIView): body % user.confirmation_key, "noreply@flashy.cards", [user.email]) + user = authenticate(username=email, password=request.data['password']) + print user + login(request, user) return Response(UserSerializer(User).data) diff --git a/flashcards/migrations/0001_initial.py b/flashcards/migrations/0001_initial.py new file mode 100644 index 0000000..82afc9d --- /dev/null +++ b/flashcards/migrations/0001_initial.py @@ -0,0 +1,157 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import django.contrib.auth.models +import django.utils.timezone +from django.conf import settings +import django.core.validators +import simple_email_confirmation.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('auth', '0007_customuser_extensionuser'), + ] + + operations = [ + migrations.CreateModel( + name='User', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(null=True, verbose_name='last login', blank=True)), + ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, max_length=30, validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.', 'invalid')], help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', unique=True, verbose_name='username')), + ('first_name', models.CharField(max_length=30, verbose_name='first name', blank=True)), + ('last_name', models.CharField(max_length=30, verbose_name='last name', blank=True)), + ('email', models.EmailField(max_length=254, verbose_name='email address', blank=True)), + ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), + ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), + ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), + ('groups', models.ManyToManyField(related_query_name='user', related_name='user_set', to='auth.Group', blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', verbose_name='groups')), + ], + options={ + 'abstract': False, + 'verbose_name': 'user', + 'verbose_name_plural': 'users', + }, + bases=(models.Model, simple_email_confirmation.models.SimpleEmailConfirmationUserMixin), + managers=[ + (b'objects', django.contrib.auth.models.UserManager()), + ], + ), + migrations.CreateModel( + name='Flashcard', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('text', models.CharField(help_text=b'The text on the card', max_length=255)), + ('pushed', models.DateTimeField(help_text=b'When the card was first pushed', auto_now_add=True)), + ('material_date', models.DateTimeField(help_text=b'The date with which the card is associated')), + ('is_hidden', models.BooleanField(default=False)), + ('hide_reason', models.CharField(help_text=b'Reason for hiding this card', max_length=255, blank=True)), + ('author', models.ForeignKey(to=settings.AUTH_USER_MODEL)), + ], + options={ + 'ordering': ['-pushed'], + }, + ), + migrations.CreateModel( + name='FlashcardMask', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('ranges', models.CharField(max_length=255)), + ], + ), + migrations.CreateModel( + name='LecturePeriod', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('week_day', models.IntegerField(help_text=b'0-indexed day of week, starting at Monday')), + ('start_time', models.TimeField()), + ('end_time', models.TimeField()), + ], + ), + migrations.CreateModel( + name='Section', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('department', models.CharField(max_length=50)), + ('course_num', models.CharField(max_length=6)), + ('course_title', models.CharField(max_length=50)), + ('instructor', models.CharField(max_length=50)), + ('quarter', models.CharField(max_length=4)), + ], + options={ + 'ordering': ['-quarter'], + }, + ), + migrations.CreateModel( + name='UserFlashcard', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('pulled', models.DateTimeField(help_text=b'When the user pulled the card', null=True, blank=True)), + ('unpulled', models.DateTimeField(help_text=b'When the user unpulled this card', null=True, blank=True)), + ('flashcard', models.ForeignKey(to='flashcards.Flashcard')), + ('mask', models.ForeignKey(help_text=b"A mask which overrides the card's mask", to='flashcards.FlashcardMask')), + ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), + ], + options={ + 'ordering': ['-pulled'], + }, + ), + migrations.CreateModel( + name='UserFlashcardReview', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('when', models.DateTimeField()), + ('blanked_word', models.CharField(help_text=b'The character range which was blanked', max_length=8, blank=True)), + ('response', models.CharField(help_text=b"The user's response", max_length=255, null=True, blank=True)), + ('correct', models.NullBooleanField(help_text=b"The user's self-evaluation of their response")), + ('user_flashcard', models.ForeignKey(to='flashcards.UserFlashcard')), + ], + ), + migrations.AlterUniqueTogether( + name='section', + unique_together=set([('department', 'course_num', 'quarter', 'instructor')]), + ), + migrations.AddField( + model_name='lectureperiod', + name='section', + field=models.ForeignKey(to='flashcards.Section'), + ), + migrations.AddField( + model_name='flashcard', + name='mask', + field=models.ForeignKey(blank=True, to='flashcards.FlashcardMask', help_text=b'The default mask for this card', null=True), + ), + migrations.AddField( + model_name='flashcard', + name='previous', + field=models.ForeignKey(blank=True, to='flashcards.Flashcard', help_text=b'The previous version of this card, if one exists', null=True), + ), + migrations.AddField( + model_name='flashcard', + name='section', + field=models.ForeignKey(help_text=b'The section with which the card is associated', to='flashcards.Section'), + ), + migrations.AddField( + model_name='user', + name='sections', + field=models.ManyToManyField(to='flashcards.Section'), + ), + migrations.AddField( + model_name='user', + name='user_permissions', + field=models.ManyToManyField(related_query_name='user', related_name='user_set', to='auth.Permission', blank=True, help_text='Specific permissions for this user.', verbose_name='user permissions'), + ), + migrations.AlterUniqueTogether( + name='userflashcard', + unique_together=set([('user', 'flashcard')]), + ), + migrations.AlterIndexTogether( + name='userflashcard', + index_together=set([('user', 'flashcard')]), + ), + ] diff --git a/flashcards/migrations/__init__.py b/flashcards/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/flashcards/models.py b/flashcards/models.py index 49dcc8b..f84a97f 100644 --- a/flashcards/models.py +++ b/flashcards/models.py @@ -1,47 +1,14 @@ -from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin +from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin, AbstractUser from django.contrib.auth.tests.custom_user import CustomUser from django.db.models import * from simple_email_confirmation import SimpleEmailConfirmationUserMixin +# Hack to fix AbstractUser before subclassing it +AbstractUser._meta.get_field('email')._unique = True -class UserManager(BaseUserManager): - def create_user(self, email, password=None): - """ - Creates and saves a User with the given email, date of - birth and password. - """ - if not email: - raise ValueError('Users must have an email address') - - user = self.model(email=self.normalize_email(email)) - - user.set_password(password) - user.save(using=self._db) - return user - - def create_superuser(self, email, password): - """ - Creates and saves a superuser with the given email and password. - """ - user = self.create_user(email, password=password) - user.is_staff = True - user.save(using=self._db) - return user - - -class User(AbstractBaseUser, SimpleEmailConfirmationUserMixin, ): +class User(AbstractUser, SimpleEmailConfirmationUserMixin, ): USERNAME_FIELD = 'email' REQUIRED_FIELDS = [] - - objects = UserManager() - is_staff = BooleanField(default=False) - - email = EmailField( - verbose_name='email address', - max_length=255, - unique=True, - ) - date_joined = DateTimeField(auto_now_add=True) sections = ManyToManyField('Section') @@ -52,7 +19,7 @@ class UserFlashcard(Model): 2. A user used to have a flashcard in their deck 3. A user has a flashcard hidden from them """ - user = ForeignKey(User) + user = ForeignKey('User') mask = ForeignKey('FlashcardMask', help_text="A mask which overrides the card's mask") pulled = DateTimeField(blank=True, null=True, help_text="When the user pulled the card") flashcard = ForeignKey('Flashcard') diff --git a/flashcards/serializers.py b/flashcards/serializers.py index 66a3ad2..bdc8a11 100644 --- a/flashcards/serializers.py +++ b/flashcards/serializers.py @@ -18,10 +18,8 @@ class LecturePeriodSerializer(HyperlinkedModelSerializer): class UserSerializer(HyperlinkedModelSerializer): - """ - """ email = EmailField(required=False) class Meta: model = User - fields = ("email", "is_active", "last_login", "date_joined") + fields = ("email", "is_active", "last_login", "date_joined") \ No newline at end of file diff --git a/flashy/settings.py b/flashy/settings.py index c48e818..45404c4 100644 --- a/flashy/settings.py +++ b/flashy/settings.py @@ -56,7 +56,10 @@ MIDDLEWARE_CLASSES = ( ) ROOT_URLCONF = 'flashy.urls' - +# Authentication backends +AUTHENTICATION_BACKENDS = ( + 'django.contrib.auth.backends.ModelBackend', +) TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', -- 1.9.1