How To: Django Contact Form

06/09/2019 12:06 By Dominic DiTaranto

So I have just recently started learning Django. Coming from Flask, almost all aspects of it have been quite simple to figure out. Of course, Corey Schafer's Django tutorial series is my go to guide. 

I am currently developing a website for a non-profit organization supporting those with allergies in New Jersey. I needed to create a contact form and assumed it would be an easy task, considering I have done it in Flask many times. I watched a few YouTube videos, looked at some stack exchange posts and got many different explanations, even one video only showing how users can send their message directly to a database instead of sending it as an email. 

Anyway, because I was unable to find a concise explanation easily, I will offer my solution (an amalgamation of a few YouTube videos and blog posts I just learned from) here for those who want the quick and dirty way of handling a contact form in Django.

1. Setup

I am not going to go through the preliminary steps of setting up a Django application, so let's get straight into the configuration of the email.

In your settings.py file add these lines of code and configure them to your needs:

EMAIL_USE_TLS = True
EMAIL_HOST = 'Your Email Host'
EMAIL_PORT = Email Port Number
EMAIL_HOST_USER = 'Your Email Address'
EMAIL_HOST_PASSWORD = 'Your Email Password'

**The  TLS setting and PORT setting will vary depending on your email host. But if you are planning on using gmail, this would be the proper configuration:

EMAIL_USE_TLS = True
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_HOST_USER = 'youremail@gmail.com'
EMAIL_HOST_PASSWORD = 'PASSWORD'

Furthermore, we should not be storing password and email addresses like this. I suggest you use environment variables instead to properly hide sensitive data. A proper explanation of the implementation of environment variables is beyond the scope of this tutorial, but I do recommend you check out this video to learn more.

2. Forms

In your app directory, create a file named forms.py and add this snippet of code:

from django import forms


class Contact(forms.Form):
    name = forms.CharField(required=True)
    email = forms.EmailField(required=True)
    subject = forms.CharField(required=True)
    message = forms.CharField(required=True, widget=forms.Textarea)

Here we will ask the user for their name, email, the subject of their message and the actual content of the message itself.

3. Views

In your views you need to add these imports:

from django.shortcuts import render, redirect
from django.core.mail import send_mail
from .forms import Contact

We will be using this send_mail() function from django.core.mail to send the email retrieved from the form filled out by the user. Its documentation can be found here: https://docs.djangoproject.com/en/2.2/topics/email/#send-mail

Now we need to create the view for the contact form.

def contact(request):

    if request.method == 'POST':
        form = Contact(request.POST)

        if form.is_valid():

            message = """ 
            From: %s <%s>
            %s
            """ % (form.data['name'], form.data['email'], form.data['message'])

            send_mail(
                form.data['subject'],
                message,
                form.data['email'],
                ['domdit@gmail.com'],
                fail_silently=False
            )

            return redirect('index')

    else:
        form = Contact()

    context = {
        'form': form,
    }

    return render(request, 'contact.html', context)

In the view we are checking first whether or not the request method was POST. If it is, we make sure that the form was filled out correctly with the if_valid() function. then we create the body of the message. It is important to note that I am using the old style of string formatting here by using the % operator. This is just my own personal preference. the send_mail() function does not include an argument for the user's name, so instead we add it to the message variable, along with their email next to it (for safe keeping and easy readability), and then on the next line we add the complete message itself.

After calling the send_mail(subject, message, email_of_user, your_email) function, we redirect the user to whatever view you so please. here I am sending them to the index, and will probably flash a message saying "Message Successfully Sent!" (will be added in Part 2)

4. Template

The contact template is just as easy as the previous steps. Just create a contact.html file in your app/templates directory and copy-paste this in there.

<h1>Contact</h1>
<form role="form" action="" method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Submit</button>
</form>

{{ form.as_p }} puts each form field into a <p> tag, but you should check out Crispy-Forms for better ways to style your form fields!

And that is it. You can now have users send you an email.  

Continue Reading...
Category: Coding
Tags: Python, Django, Contact, Email

Asteroids in p5.js

06/08/2019 08:30 By Dominic DiTaranto

I recently created an Asteroids game with the p5.js library! It can be played at http://www.asteroids.domdit.com and here is the github repo: https://github.com/domdit/p5js-asteroidsv2 . It is pretty crazy how much fun I am having playing this game.

You can listen to the soundtrack here in a small EP that I compiled called "Amphibians and Asteroids." 

I really wish I took more courses in creative coding, but learning has never been easier with Daniel Shiffman's Coding Train!

A few screenshots of the game:

Start Screen

 

game-play 1

game-play 1

Continue Reading...
Category: Coding
Tags: Python, p5.js, Asteroids, javascript, music

First Post

05/08/2019 11:50 By Dominic DiTaranto

It feels weird to begin writing the first post of this blog. I spent the last month creating the blogging engine, Alambi, that is powering this page; however, not once did I think about what I was actually going to post once v.1 was completed. There were a bunch of challenges creating Alambi. I started learning Python about 5 months ago, so the whole creation process has been a learning experience for me.  A lot of things that I originally thought would have been simple to implement turned out to take the most time, and what I feared would take a week to complete were done in a matter of seconds. A great example of both situations would be implementing a many-to-many database relationship in SQLAlchemy in order to manage the tags fo a blog post. The Flask-SQLAlchemy documentation clearly lays out how to implement this type of relationship with this example:

 

tags = db.Table('tags',
    db.Column('tag_id', db.Integer, db.ForeignKey('tag.id'), primary_key=True),
    db.Column('page_id', db.Integer, db.ForeignKey('page.id'), primary_key=True)
)

class Page(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    tags = db.relationship('Tag', secondary=tags, lazy='subquery',
        backref=db.backref('pages', lazy=True))

class Tag(db.Model):
    id = db.Column(db.Integer, primary_key=True)

Source: https://flask-sqlalchemy.palletsprojects.com/en/2.x/models/

And guess what, not surprisingly, it works just as it says.  But you would never know that unless you actually understood how to add and get to the tags that are stored in the database (valuable information not available in the documentation linked above)! Before I get into that, let me show you how I implemented the many-to-many relationship above in Alambi (for clarity).

alambi/models.py

tags = db.Table('tags',
                db.Column('blog_id', db.Integer, db.ForeignKey('blog.blog_id')),
                db.Column('tag_id', db.Integer, db.ForeignKey('tag.tag_id'))
                )


class Blog(db.Model):
    blog_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(200), unique=False, nullable=False)
    date = db.Column(db.DateTime, default=datetime.datetime.utcnow)
    text = db.Column(db.Text)
    like = db.Column(db.Integer, default=0)
    sticky = db.Column(db.Boolean, default=False)
    category = db.Column(db.String(200), default='No Category')
    comments = db.relationship("Comment", backref='post', cascade="all, delete-orphan")
    comment_count = db.Column(db.Integer, default=0)
    tags = db.relationship('Tag', secondary=tags,
                           backref=db.backref('post_tags', lazy='dynamic'))
    

class Tag(db.Model):
    tag_id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(200))

One thing to notice is that the primary key column of both the Blog  and Tag tables, which I usually just call "id", needed to be distinguished from eachother to avoid errors. Thus they are named blog_id and tag_id respectively. 

Okay, so the tables are connected with the 'tags' association table, let's try to insert a dummy post in the python interpreter to make sure everything is working fine.

First, let's take a look at the wrong way:

>>>post = Blog(name="First Post", text="this is my first post", category="Talking", tags=['tags', 'test'])

Alas, this throws the error: AttributeError: 'str' object has no attribute '_sa_instance_state'. So we know something is wrong with just using a list of strings in the tags column (I might need to be more clear, the error thrown does not actually say anything about which column is throwing the error.  but considering "tags" is what we are focusing on here and the other columns are just getting strings, we can infer the issue is with the 'tags' row). Why doesn't this work? It seems like a list would be the logical choice here. But we have to remember that the tags are actually being handled by the 'Tag' table, and are just referenced in the 'Blog' table. Therefore, we must input them as objects in order for them to properly be handled by the Tag table prior to insertion into the Blog table.

Now, let's see the correct way:

>>>post = Blog(name="First Post", text="this is my first post", category="Talking", tags=[Tag(name='foo'), Tag(name='bar')])
>>>db.session.add(post)
>>>db.session.commit()

What should pop out first to you is that when adding tags to a post, we need to add them as their own objects. This is something that was not immediately clear to me, but was able to reverse engineer my understanding by looking at the code in Learning Flask Framework  by Matt Copperwaite and Charles Leifer on page 37-42. I do not know if I can actually post their code here due to copyright, but I do recommend taking a look at their book to get a better grasp on how comma separated values can be taken from a Flask-WTForms string field  and correctly inserted into a many-to-many db. Either way, what we need to take away from here is that, in this case, the backref column has data input as a list of objects, rather than al ist of strings. Do not forget to add 'post' and commit it!

Okay, that was not so bad, the rest will be just as easy. We can view the tags in the python interpreter as such:

>>> post.tags
[<Tag 1>, <Tag 2>]

And to see their names is just as simple:

>>> post.tags[0].name
'foo'
>>> post.tags[1].name
'bar'

Practical Implementation

What if we wanted to post the tags for a specific post on a Flask website. First, in our view, we need to query the post that we want to display. For now, let's just grab the first post in our Blog table:

routes.py

@app.route("/post")
def post(post_id):
    post = Blog.query.first()
    return render_template('post.html', post=post)

Then, in our post.html template we can display the information like this:

post.html

    {# Name of Post #}
    <h1>{{post.name|safe}}</h1>

    {# Date of Post #}
    <small>{{post.date.strftime('%m/%d/%Y %I:%M')|safe}} by Dominic DiTaranto</small>

    {# post content #}
    <p>{{post.text|safe}}</p>

    {# Tags #}
        <b>Tags:</b>
            {% for tag in post.tags %}
                {{tag.name}} {%if not loop.last%}, {%else%} {%endif%}
            {% endfor %}

Of course, we also want to put the name, date, and actual content of the post as well, so I added that in while still trying to remain as minimal as possible for pedagogical purposes. Moving to the {# Tags #} section, we can see a simple for loop in Jinja2 which just grabs the name of the tags for each tag associated with the specific post we are displaying. The if statement following the tag name watches whether or not the loop is on its final iteration, and if it is, will omit the comma after the tag.  You can see an example of this in action by looking at the bottom of this post and finding the "Tags" section.

I hope this was informative and understandable. As this is my first time trying to formally explain programming, I invite any and all criticism!

Continue Reading...
Category: Coding
Tags: Python, Web Development, Contact Form, First-Post!, SQLAlchemy, Jinja2, Flask

DOMDIT.COM

Hello and welcome to my blog. I am planning to post from a myriad of different topics including: coding, translations, reflections on books I am currently reading, music that I am writing, photography, as well as personal topics/events that may creep in once in a while. To learn more about me, you can visit the main page of this website here, or you can always comment on one of my posts and I will get back to you!

Recent Posts:

How To: Django Contact Form
06/09/2019 12:06
Asteroids in p5.js
06/08/2019 08:30
First Post
05/08/2019 11:50

Popular Posts:

First Post
05/08/2019 11:50
Asteroids in p5.js
06/08/2019 08:30
How To: Django Contact Form
06/09/2019 12:06

Recent Categories:

  • Coding

  • Recent Tags:

    Asteroids Contact Contact Form Django Email First-Post! Flask Jinja2 Python SQLAlchemy Web Development javascript music p5.js
    1
    Powered by Alambi | Designed by Dominic DiTaranto © 2019