- Project structure
- Manage dev and production settings
- Create a slug
- Send email
- Database dump to file
- Provide data to DB via Django Python Shell
- Create script with access to Django shell
- Migrate Django from SQLite to PostgreSQL
- Using Django Messages with Bootstrap
- Override form
__init__
method - Switch to a new Database
Below is an example of a structure for a large project that contains a Django app.¹ ²
project/
│
├── app/
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ │
│ ├── migrations/
│ │ └── __init__.py
│ │
│ ├── models.py
│ ├── tests.py
│ └── views.py
│
├── docs/
│
├── project/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
│
├── static/
│ └── style.css
│
├── templates/
│ └── base.html
│
├── .gitignore
├── manage.py
├── LICENSE
└── README.md
When working with Django it is often useful/needed to have different settings for development and production. One way to handle this is to have separate setting files for each case.¹ ² For instance, settings.py
for production and settings_dev.py
for development. The DJANGO_SETTINGS_MODULE
environment variable controls which settings file Django will load. So, in development, we can:
set DJANGO_SETTINGS_MODULE=mysite.settings_dev
python manage.py runserver
Alternatively we can speciffy the settings file when calling the manage.py
:
set DJANGO_SETTINGS_MODULE=mysite.settings_dev
python manage.py runserver --settings=settings_dev
However, this will not work when doing migrations. So if you required different settings when migrating, the first methods is probably better.
Call the Django slugify
function automatically by overriding the save
method. It is preferable to generate the slug only once when you create a new object, otherwise your URLs may change when the q
field is edited, which can cause broken links. More info here.
# models.py
from django.utils.text import slugify
class Test(models.Model):
q = models.CharField(max_length=30)
s = models.SlugField()
def save(self, *args, **kwargs):
if not self.id:
# Newly created object, so set slug
self.s = slugify(self.q)
super(Test, self).save(*args, **kwargs)
If html_message
keyword argument is provided, the resulting email will be a multipart/alternative email with message
as the text/plain content type and html_message
as the text/html content type.
# views.py
from django.core.mail import send_mail
send_mail(
'Subject here',
'Here is the message.',
'from@example.com',
['to@example.com'],
fail_silently=False,
)
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. More info here.
Save from DB
$ python manage.py dumpdata > db_dump.json
Load fixture to DB
$ python manage.py loaddata <fixture>
Exemple for loading data in a json file named filename.json
to the Model Member
in the app website
$ python manage.py shell
>>> import json
>>> from website.models import Member
>>> with open('filename.json', encoding="utf-8") as f:
... members_json = json.load(f)
...
>>> for member in members_json:
... member = Member(name=member['name'], position=member['position'], alumni=member['alumni'])
... member.save()
...
>>> exit()
If you want to run an external script but have access to the Django environment like you do with python manage.py shell
you can do the following. More info here
# your_script.py
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "your_project_name.settings")
# your imports, e.g. Django models
from your_project_name.models import Location
# From now onwards start your script..
Here is an example to access and modify your model:
# models.py
class Location(models.Model):
name = models.CharField(max_length=100)
# your_script.py
if __name__ == '__main__':
# e.g. add a new location
l = Location()
l.name = 'Berlin'
l.save()
# this is an example to access your model
locations = Location.objects.all()
print locations
# e.g. delete the location
berlin = Location.objects.filter(name='Berlin')
print berlin
berlin.delete()
Alternatively
Create your script and run it from within the python shell:
$ python manage.py shell
>>> exec(open("filename.py").read())
This will read and run the contents of the file. Works on Python 3.
Here's how to migrate a Django database from SQLite to PostgreSQL. More info here.
- Dump existing data:
python manage.py dumpdata > datadump.json
-
Change settings.py to Postgres backend.
-
Make sure you can connect on PostgreSQL. Then:
python manage.py migrate --run-syncdb
- Run this on Django shell to exclude contentype data
python manage.py shell
>>> from django.contrib.contenttypes.models import ContentType
>>> ContentType.objects.all().delete()
>>> quit()
- Finally:
python manage.py loaddata datadump.json
Configure the Django Messages Framework to work with Bootstrap by changing the MESSAGE_TAGS
. In the settings.py
file:
from django.contrib.messages import constants as messages
MESSAGE_TAGS = {
messages.DEBUG: 'info',
messages.INFO: 'info',
messages.SUCCESS: 'success',
messages.WARNING: 'warning',
messages.ERROR: 'danger',
}
In your HTML base template insert the section where messages will display:
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
{{ message }}
</div>
{% endfor %}
{% endif %}
To use messages do the following in views.py
:
from django.contrib import messages
messages.debug(request, '%s SQL statements were executed.' % count)
messages.info(request, 'Three credits remain in your account.')
messages.success(request, 'Profile details updated.')
messages.warning(request, 'Your account expires in three days.')
messages.error(request, 'Document deleted.')
You can change how a form is created base on logic from a view by modifying the form __init__
method.
# forms.py
class TransactionForm(forms.ModelForm):
class Meta:
model = Transaction
fields = ('category', 'name', 'value', 'split')
# Override init method so that the split field only shows in forms of account with more than one user
def __init__(self, *args, **kwargs):
multiple_users = kwargs.pop('multiple_users', True)
super(TransactionForm, self).__init__(*args, **kwargs)
if not multiple_users:
del self.fields['split']
In the views.py
file you pass the multiple_users
variable to the form class
# views.py
def transaction_new(request, account_id):
if request.method == "POST":
form = TransactionForm(request.POST, multiple_users=multiple_users)
if form.is_valid():
transaction.save()
return redirect('transaction_new', account_id=account_id)
else:
form = TransactionForm(multiple_users=multiple_users)
context = {...}
return render(request, 'expense_tracker/new_transaction.html', context)
Edit your settings.py
and add the new database to the DATABASES list and give it the name "new". For example, I am using the MySQL database, so I will add the "new" dictionary:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
},
'new': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'tFnXEEpcjQ',
'USER': 'tFnXEEpcjQ',
'PASSWORD': 'UW9BWCT8m2',
'HOST': 'remotemysql.com',
'PORT': '3306',
}
}
I have used the MySQL database in this example. You will add the code for the database that you are using. Note: You need to install the client for whatever database you are using on your machine.
Make sure that you have not deleted the migrations folder. If you have deleted it by accident, make migrations again.
To create tables in your new database, run the migrations on it using the following command:
python manage.py migrate --database=new
You can skip this step if you do not want to transfer the data from your old database to the new one.
First, clear the new database by running the following command:
python manage.py flush --database=new
Export data from your current database to a JSON file using the following command:
python manage.py dumpdata>data.json
Now load data into the new database using the following command:
python manage.py loaddata data.json --database=new
Now remove the old database from settings.py
and rename the "new" database to "default". My final DATABASES
look like this:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'tFnXEEpcjQ',
'USER': 'tFnXEEpcjQ',
'PASSWORD': 'UW9BWCT8m2',
'HOST': 'remotemysql.com',
'PORT': '3306',
}
}