The environment.yml indicates the tools I used in this practice.
This project is my second django project. It is a step-by-step practice associtated to the youtube tutorial by Corey Schafer.
The author tries to introduce the some common concepts within django framework and guide audiences to build a blog system hand-by-hand. Source code .
We have added basic display blog and support user to modify profile with username, email, profile image. New concepts used:
- introduce
django-crispy-formmodule to handle the form feedbacks and validation. Set up tutorial. - Profile and User is one-to-one relationship.
models.OneToOneField(on_delete=models.CASCADE) - use Pillow module to handle the ImageField type. Pillow documentation.
- Resize the uploaded image by overriding the
save()method inProfileClass using theuser.profile.image.path - Decorator
@login_requiredfromfrom django.contrib.auth.decoratorsused when displaying user profile enctype="multipart/form-data"needed to save image file by form POST method
Implement the blog post list,post detail,post create,post update, post delete by using class based view (ListView,DeteailView,CreateView,UpdateView,DeleteView)
New concepts introduced:
- Classes inheriting from class-based view have its default attribute and method as well as its default template name.
ListViewandDetailViewwill search for<app_name>/<model_name>_viewtype.html, whileCreateViewandUpdatewill locate<app_name>/<model_name>_form.html,DeleteViewwill look for<app_name>/<model_name>_confirm_delete.html.model = <Model_name>is the same withquery_set = <Model_name>.objects.all(), 'fields' will indicate the form fields in theUpdateViewandCreateView- After creating (
CreateView)a post, thePostshould define itsget_absolute_urlto indicate the url that will redirect to. - When updating and creating a post, need to set the author of the post to the login user(Foreign Key) within the overriding
form_valid()method. UpdateViewandDeleteViewneed to require the user authentication in which the author of post needs to be equal with the login user.UserPassesTestMixinand overridingtest_func()do the job.
Introduce and implement the pagination in Blog. Create new ListView with custom filted queries and an useful route for posts written by specific author.
New concepts:
-
django.views.generic.list.ListViewprovides a builtin way to paginate the displayed list. You can do this by adding apaginate_byattribute to your view class.This limits the number of objects per page and adds apaginatorandpage_objto the context.orderingcan indicate the displaying sequence. -
By overriding the
get_queryset()method, we can add customized filter to the object in the context. Paring with defining filter route, we can implement other customized filter .
Email and password reset.Send a email with a link to reset password to gmail account.
Mail is sent using the SMTP host and port specified in the EMAIL_HOST and EMAIL_PORT settings. The EMAIL_HOST_USER and EMAIL_HOST_PASSWORD settings, if set, are used to authenticate to the SMTP server, and the EMAIL_USE_TLS and EMAIL_USE_SSL settings control whether a secure connection is used.
New concepts:
PasswordResetViewfrom auth.views : reset password through emailPasswordResetDoneViewfrom auth.views : indicate the action when the request is done.PasswordResetConfirmView: this view generatesuidb64andtokenparameters for security check every time. So the route needs to consider these parameters.PasswordResetCompleteViewshow the render template when the reset is complete.- The email that the reset link will send to must be the same with user.email inthe database. Otherwise, Django will not send the email.
- set environment variable .
conda env config vars [list/set/unset]and reactivate the environment to make changes take effect.
How to deploy the application to web server. Django deployment checklist Options:
- Linux server:(Linode)
- The first thing we need to do is set up the remote server environment:
adduser,change directory and files mode, collect the package requirement and dependency. - Install webserver:
Apacheornginx.sudo apt-get install apache2,sudo apt-get install libapache2-mod-wsgi-py3. - Then configure the webserver:
cd /etc/apache2/sites-avaiableand copy the000-default.confto project directory and renameproject_name.conf.SetAlias /static /home/<username>/<project_name>/static - Add <Directory <path_topath>> Require all granted
static,media,wsgi.py,WSGIScriptAlias / <full_path_to_wsgi.py>,WSGIDaemon Process django_app python-path=<full_path_to> pyt$ python-home=<full_path_to_enve>,WSGIProcessGroup django_app sudo a2ensite <django_project_name>enable the site and disable the000-default.conf- change the owner of
<project_name>/db.sqlite3:sudo chown :www-data <project_name>/db.sqlite3andsudo chmod 664 <project_name>/db.sqlite3and change the group owner towww-data:sudo chown :www-data <project_name>.And the same process tomediafolder .
- The first thing we need to do is set up the remote server environment:
Setting changes in Django project:
ALLOW_HOSTS- In production, we need to set up the
STATIC_ROOT=os.path.join(BASE_DIR,'static')and runpyhton manage.py collectstatic - create a configure json file to handle secret info:
SECRET_KEYand the environment variablesEMAIL_USER,EMAIL_PASSin this project. - load the json file in
settings.py:with open('<JSON_FILE>') as config_file: config = json.load(config_file)
- Set
SECRET_KEY = congif['SECRET_KEY'],DEBUG=Falseand update other environment virables(EMAIL_USERandEMAIL_PASS)
Use a custom domain name for our application and enbale HTTPS with a free SSL/TLS Certificate using Let's Encrypt
-
register a domain, set the domain DNS server. and add DNS records(
*,www) in server manager. -
add the domain name to
ALLOWED_HOSTSinsettings.py -
Start to select the webserver framework and operating system. Get your site on HTTPS. Follow the instructions to install neccessary packages.
-
Update the Apache configure file:
<project_name>.conf: add theServerName :....comment out theWSGISCriptAlias,WSGIDaemonProcess,WSGIProcessGroup -
sudo cerbot --apache -
add the
httpsto utf allow. -
Since the certificate needs to be renewed every 90 days, so we need to automatically renew the task:
```sh sudo cerbot renew --dry-run sudo crontab -e #m h dom mon dow command 30 4 1 * * sudo cerbot renew --quiet ```
Using AWS S3 for File Uploads.Django storage S3 Docs
Save the secret info from AWS S3 in the configure file.
Change the settings.py:
# add the INSTALLED_APP: storages
AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY_ID')
AWS_ACCESS_ACCESS_KEY = os.environ.get('AWS_ACCESS_ACCESS_KEY')
AWS_STORAGE_BUCKET_NAME = os.environ.get('AWS_STORAGE_BUCKET_NAME')
AWS_S3_FILE_OVERWRITE = False
AWS_DEFAULT_ACL = None
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'Need to remove the save()method in users models since the Pillow conflicts with usage of AWS S3 storages.
Configuring Django Apps for Heroku
Remember to add sudo when facing permission denied issues!!!
-
Get a Heroku acount and Login
sudo heroku login(Open the browser to login) orsudo heroku login -i(Login in the CLI) -
Create and upload the website.
heroku create <app_name>This creates a git remote ("pointer to a remote repository") named heroku in our local git environment.
- Push file to Heroku
git push heroku mainwill push the files to the heroku remote,it will intall all packages in requirements.txt.
In order to execute your application Heroku needs to be able to set up the appropriate environment and dependencies, and also understand how it is launched. For Django apps we provide this information in a number of text files:
runtime.txt: the programming language and version to use.requirements.txt: the Python component dependencies, including Django.Procfile: A list of processes to be executed to start the web application. For Django this will usually be the Gunicorn web application server (with a .wsgi script).wsgi.py: WSGI configuration to call our Django application in the Heroku environment.
requirements.txt:
django==3.1.2
dj-database-url==0.5.0
django-crispy-forms==1.12.0
django-herokuMost importantly, Heroku web applications require a Procfile.
This file is used to explicitly declare your application’s process types and entry points. It is located in the root of your repository.This Procfile requires Gunicorn, the production web server that we recommend for Django applications.
Set Procfile:
web:gunicorn <project_name>.wsgi
Be sure to add gunicorn to your requirements.txt file as well.
-
install and use
git.set.gitignorefile Github gitignore add.DS_Storeto ignore file. Commit all the file to local tree. -
On Heroku, sensitive credentials are stored in the environment as
config vars.Thedjango-herokupackage automatically configures your Django application to work on Heroku. It is compatible with Django 2.0 applications.
Django-heroku Installer: conda install -c conda-forge django-heroku
Be sure to add django-heroku to your requirements.txt file as well.
Set heroku configure: heroku config:set <VARS_NAME> = <VARS_VALUE>
Add the following import statement to the top of settings.py:
import django_heroku
# Activate Django-Heroku.
django_heroku.settings(locals())
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.9/howto/static-files/
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATIC_URL = '/static/'
# Extra places for collectstatic to find static files.
STATICFILES_DIRS = (
os.path.join(BASE_DIR, 'static'),
)Django won’t automatically create the target directory (STATIC_ROOT) that collectstatic uses, if it isn’t available. You may need to create this directory in your codebase, so it will be available when collectstatic is run. Git does not support empty file directories, so you will have to create a file inside that directory as well.
- We use
sqlite3in development environment and transfer toPostgresin production.heroku addons:createcan create any database heroku supports.heroku pgwill show the detail aboutpostgresqlheroku has installed
heroku run python manage.py migrate can migrate the database tables that already exists in the dev environment.
or use the heroku bash command line:
heroku run bash
python manage.py createsuperuser
exit
heroku open
-
Add the
<project_name>.herokuapp.comtoALLOWED_HOSTS -
Debugging.The Heroku client provides a few tools for debugging:
# Show current logs
heroku logs
# Show current logs and keep updating with any new results
heroku logs --tail
# Add additional logging for collectstatic (this tool is run automatically during a build)
heroku config:set DEBUG_COLLECTSTATIC=1
# Display dyno status
heroku ps