5
\$\begingroup\$

I am learning Python (3) and Django (1.7) after a while in PHP (not POO). I am rebuilding my own website that has public members. My members have a user account, a public profile and a private profile. When a member signs in, an email is sent to the admins to say that a new member has joined the team and a new email is sent to the new member to say hello.

I am quite new with Django and did all my saves and email sending in the view. I have the idea that it's not the best practice. Can you help me improve my code?

models.py

class UserProfile(models.Model):
    user = models.OneToOneField(User)
    slug = models.SlugField(max_length=30)
    avatar = models.ImageField(null=True, blank=True, upload_to="images/")

    def __str__(self):
        return self.user

class UserProfilePublic(models.Model):
    FREQUENCY_CHOICES = (
        ('', ''),
        ('1', 'Moins de 2 romans'),
        ('2', 'Entre 2 et 5 romans'),
        ('3', 'Entre 6 et 10 romans'),
        ('4', 'Plus de 2 romans')
    )

    user = models.OneToOneField(User)
    website = models.URLField(blank=True)
    facebook = models.URLField(blank=True)
    twitter = models.URLField(blank=True)
    google = models.URLField(blank=True)
    hobbies = models.CharField(max_length=250, blank=True)
    books_frequency = models.CharField(max_length=1, choices=FREQUENCY_CHOICES, default='')
    favorite_types_of_books = models.TextField(blank=True)
    favorite_books = models.TextField(blank=True)
    quote = models.CharField(max_length=250, blank=True)

    def __str__(self):
        return self.user

class UserProfilePrivate(models.Model):
    user = models.OneToOneField(User)
    address = models.CharField(max_length=250, blank=True)
    address_bis = models.CharField(max_length=250, blank=True)
    zipcode = models.CharField(max_length=5, blank=True)
    address_bis = models.CharField(max_length=30, blank=True)

    def __str__(self):
        return self.user

forms.py

class RegisterForm(forms.Form):
    username = forms.CharField(min_length=5, max_length=30, required=True)
    email = forms.EmailField(max_length=75, required=True)
    password = forms.CharField(widget=forms.PasswordInput, required=True)
    password_check = forms.CharField(widget=forms.PasswordInput, required=True)

    def clean(self):
        cleaned_data = super(RegisterForm, self).clean()
        username = cleaned_data.get('username')
        email = cleaned_data.get('email')
        password = cleaned_data.get('password')
        password_check = cleaned_data.get('password_check')
        recaptcha = cleaned_data.get('g-recaptcha-response')

        # Username
        if User.objects.filter(username=username).exists():
            msg = "Ce nom d'utilisateur n'est pas disponible"
            self.add_error('username', msg)

        # Email
        if User.objects.filter(email=email).exists():
            msg = "Cet email est déjà associé à un utilisateur"
            self.add_error('email', msg)

        # Password
        if password_check != password:
            msg = "Le mot de passe et sa confirmation doivent être identiques"
            self.add_error('password_check', msg)

        return cleaned_data

views.py

def register(request):
    if request.user.is_authenticated():
        return redirect('/')
    else:
        if request.method == 'POST':
            form = RegisterForm(request.POST)

            if form.is_valid():
                username = form.cleaned_data['username'].strip().capitalize()
                email = form.cleaned_data['email']
                password = form.cleaned_data['password']
                slug = username.replace("'","-")
                slug = slugify(slug)

                #Database save
                user = User.objects.create_user(username, email, password)
                profile = UserProfile(user= user, slug= slug)
                profile.save()
                profile_public = UserProfilePublic(user= user)
                profile_public.save()
                profile_private = UserProfilePrivate(user= user)
                profile_private.save()

                #Email sent to admins
                email_from = ''
                email_to = ['', '']
                email_subject = ""
                email_message = ""

                send_mail(subject=email_subject, message=email_message, from_email=email_from, recipient_list=email_to, fail_silently=False)

                #Email sent to the new member
                email_from = ''
                email_to = [email]
                email_subject = ""
                email_message = ""

                send_mail(subject=email_subject, message=email_message, from_email=email_from, recipient_list=email_to, fail_silently=False)

                #Success message
                messages.success(request, "Votre inscription est désormais terminée. <a href=''>Connectez-vous</a> pour démarrer l'aventure.")

        else:
            form = RegisterForm()

        return render(request, 'membres/register.html', locals())
\$\endgroup\$
1
\$\begingroup\$

The code looks pretty good! There are a couple things that you might consider:

As mentioned in the comment, it might be better to create a Custom User Model, instead of your profiles. One bonus there is that Django already provides a form for user creation (UserCreationForm), which will do the pasword validation for you. You could extend that form (if necessary).

I also wonder why you're using separate models for the Public and Private profiles? I would think it would be simpler to just have them on one object, and flag some of the fields as "private"?

As you mentioned, it's best to keep your email-sending out of the view. Instead, consider using a post_save signal to send the email upon user creation. This way, if you later have an alternative way of creating users (maybe via Facebook login), it will still send an email. As an example:

from django.db.models.signals import post_save
from django.conf import settings

def send_user_emails(sender, instance, created, *args, **kwargs):
    if not created:
        # We don't want to send emails on all saves, just on create
        return  

    #Email sent to admins
    '''Django has a ADMIN setting for the emails and names of
    your admins, so you might as well just use that
    (https://docs.djangoproject.com/en/1.9/ref/settings/#admins)'''
    admin_addresses = [email for name, email in settings.ADMINS]
    send_mail(
        from='',
        recipient_list=admin_addresses,
        subject='',
        message='New member: {}'.format(instance.username),
        fail_silently=False
    )

    #Email sent to the new member
    send_mail(
        from='',
        recipient_list=[instance.email_address],
        subject='',
        message='Welcome!',
        fail_silently=False
    )

# Connect this up to your User model, or perhaps the UserProfile model
post_save.connect(send_user_emails, sender=User)

If your app gets popular, you might want to consider doing the email sending with something like Celery, since right now it's happening in the request, which could slow things down.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.