Simple Django Tutorial

Abstract

This post describes the steps required to generate a Multi-App Django Site (aka Project) that contains 2 simple Apps:

  1. Hello App – a Minimal App
  2. Contacts App

See JoeCodeswell Downloads – DjangoTutMysite.zip for a zip of the Final Code.

Content

Click here to go to content.

1. Prerequisites

  1. Installed Django 1.9+
  2. Above is Running on Python 3
  3. You need to understand the basics of urls and Views. See below.)

1.1 Brief basics of urls and Views

  1. A user enters a url into her browser.
  2. Django’s urls.py files try to match that url to a View.
  3. If a match is found the View returns the resulting HTML to the user’s browser.
    Here’s more from Django Girls
    See also, the IMPORTANT Django Doc “URL dispatcher“.

2. Create a Project i.e. a Site

N.B. Django conflates Project with Site.

2.1 Run django-admin startproject

Assume CODE_HOME stands for the directory where you’d like to store your code.

> cd CODE_HOME
> django-admin startproject mysite

Here’s the resulting file structure. N.B. The top directory, named mysite, CONTAINS a sub-directory with the SAME NAME, mysite.

CODE_HOME/
    mysite/
        manage.py
        mysite/
            __init__.py
            settings.py
            urls.py
            wsgi.py

2.2 Verify the Site

> cd CODE_HOME/mysite
> python manage.py runserver

You should see something like the following.

Performing system checks...

System check identified no issues (0 silenced).

[FOR THIS TUTORIAL, STUFF HERE, WAS DELETED.]

Django version 1.9.5, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

Browsing to http://127.0.0.1:8000/ you should see something like the following.

It worked!
Congratulations on your first Django-powered page.

2.3 Run createsuperuser

Now we run the createsuperuser command so we can use a superuser account to do Admin functions.

> cd CODE_HOME/mysite
> python manage.py createsuperuser

Output will look like this.

Username (leave blank to use 'whatever'):
Email address: w@whatever.com
Password:
Password (again):
This password is too short. It must contain at least 8 characters.
Password:
Password (again):
Superuser created successfully.

3. Hello App

Let’s make a Hello App and call it “hello”.

N.B. We will run django-admin startapp NOT startproject.
Again, assume CODE_HOME stands for the directory where you “stored your code”.
Remember the resulting tree from startproject was as follows.

CODE_HOME/
    mysite/
        manage.py
        mysite/
            __init__.py
            settings.py
            urls.py
            wsgi.py

3.1 Run django-admin startapp

> cd CODE_HOME/mysite 
> django-admin startapp hello

Here’s the resulting tree.

CODE_HOME/
    mysite/
        db.sqlite3
        manage.py
        hello/
            __init__.py          
            admin.py
            apps.py
            models.py
            tests.py
            views.py
            migrations/
                __init__.py    
        mysite/
            __init__.py
            settings.py
            urls.py
            wsgi.py

3.2 Add hello to settings.py

We need to edit the Site file, settings.py, to tell Django that our hello App exists. Add one line to CODE_HOME/mysite/mysite/settings.py as shown.

# CODE_HOME/mysite/mysite/settings.py

...
# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'hello',                     # add this line
]
...

3.3 Add an App url

We need to add our hello App to our Site urls.py file.

After the deleted boilerplate change the code in CODE_HOME/mysite/mysite/urls.py to be as shown.

# CODE_HOME/mysite/mysite/urls.py

#[FOR THIS TUTORIAL, STUFF HERE, WAS DELETED.]

from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
    url(r'^hello/', include('hello.urls', namespace='hello')), # added line
    url(r'^admin/', admin.site.urls),
]

The added line in urlpatterns, tells Django to send any url beginning with ‘hello’ to the hello App’s urls.py file for further processing.

We WILL CREATE the hello App’s urls.py file in the next step.

Here’s the Django Docs (1.9) for urls. django.conf.urls utility functions

3.4 Create our App’s urls.py file

In our Multi-App Site, each App will have its own urls.py file. We referenced that file in the include portion of the line we added to the Site urls.py file, in the previous step.

Now we need to create our hello App’s urls.py file. It will map an input url coming from a user’s browser, to a View.

Create the file CODE_HOME/mysite/hello/urls.py to be as follows.

# CODE_HOME/mysite/hello/urls.py
# This is the urls.py file for the hello App

from django.conf.urls import url
import hello.views

app_name = 'hello'
urlpatterns = [
    url(r'^$', hello.views.helloview, name='hello'), # View url    
]

The single line in urlpatterns defines the url for our View.

3.5 Create a View

Now we need to edit our hello App’s views.py file to create our View.

Edit CODE_HOME/mysite/hello/views.py.
Add lines under ‘# Create your views here.’, as shown.

from django.shortcuts import render

# Create your views here.

from django.http import HttpResponse

def helloview(request):
    return HttpResponse("Hello world!")

3.6 Test our View

If your server isn’t running already.

> cd CODE_HOME/mysite
> python manage.py runserver

In your browser, navigate to http://127.0.0.1:8000/hello/.
You should see:
Hello world!

4. Contacts App

Now let’s make a very SIMPLE Contacts App. We’ll call it ‘contacts’. It will implement CRUD functions on a database (DB). Django interfaces with a DB through Models.

We’ll follow many of the same steps we did when we made our Hello App. First, here’s the tree state of CODE_HOME, BEFORE we do anything and AFTER we made our Hello App.

. [== CODE_HOME]
+-- mysite
    +-- db.sqlite3
    +-- hello
    ¦   +-- __init__.py
    ¦   +-- __pycache__
            DELETED FOR BREVITY *.pyc files
    ¦   +-- admin.py
    ¦   +-- apps.py
    ¦   +-- migrations
    ¦   ¦   +-- __init__.py
    ¦   ¦   +-- __pycache__
                DELETED FOR BREVITY *.pyc files
    ¦   +-- models.py
    ¦   +-- tests.py
    ¦   +-- urls.py
    ¦   +-- views.py
    +-- manage.py
    +-- mysite
        +-- __init__.py
        +-- __pycache__
            DELETED FOR BREVITY *.pyc files
        +-- settings.py
        +-- urls.py
        +-- wsgi.py

4.1 Run django-admin startapp contacts

We did this BEFORE, to create our hello app. Now we’ll do it to create our contacts app

> cd CODE_HOME/mysite 
> django-admin startapp contacts

Let’s inspect our tree state now.

. [== CODE_HOME]
+-- mysite
    +-- contacts
    ¦   +-- __init__.py
    ¦   +-- admin.py
    ¦   +-- apps.py
    ¦   +-- migrations
    ¦   ¦   +-- __init__.py
    ¦   +-- models.py
    ¦   +-- tests.py
    ¦   +-- views.py
    +-- db.sqlite3
    +-- hello
    ¦   +-- __init__.py
    ¦   +-- __pycache__
            DELETED FOR BREVITY *.pyc files
    ¦   +-- admin.py
    ¦   +-- apps.py
    ¦   +-- migrations
    ¦   ¦   +-- __init__.py
    ¦   ¦   +-- __pycache__
                DELETED FOR BREVITY *.pyc files
    ¦   +-- models.py
    ¦   +-- tests.py
    ¦   +-- urls.py
    ¦   +-- views.py
    +-- manage.py
    +-- mysite
        +-- __init__.py
        +-- __pycache__
            DELETED FOR BREVITY *.pyc files
        +-- settings.py
        +-- urls.py
        +-- wsgi.py

Notice there’s no __pycache__ dir under contacts yet. I think this is because we have not yet run this with our server.

4.2 Add contacts to settings.py like for hello

As for hello, we need to edit the Site file, settings.py, to tell Django that our contacts App exists. Add one line to CODE_HOME/mysite/mysite/settings.py as shown.

# CODE_HOME/mysite/mysite/settings.py

...
# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'hello',                     # this line was added before
    'contacts',                  # add this line
]
...

4.3 Add an App url like for hello

As before for hello, we need to add our contacts App to our Site urls.py file.

Add the line in urlpatterns as shown.

# CODE_HOME/mysite/mysite/urls.py

#[FOR THIS TUTORIAL, STUFF HERE, WAS DELETED.]

from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
    url(r'^contacts/', include('contacts.urls', namespace='contacts')), # added line
    url(r'^hello/', include('hello.urls', namespace='hello')), # line was added BEFORE
    url(r'^admin/', admin.site.urls),
]

4.4 Create our App’s urls.py file

As before, in our Multi-App Site, each App will have its own urls.py file. We referenced that file in the include portion of the line we added to the Site urls.py file, in the previous step.

Now we need to create our contacts App’s urls.py file. It will map an input url coming from a user’s browser, to a View.

Create the file CODE_HOME/mysite/contacts/urls.py to be as follows.

# CODE_HOME/mysite/contacts/urls.py
# This is the urls.py file for the contacts App

from django.conf.urls import url
import contacts.views

app_name = 'contacts'
urlpatterns = [
    url(r'^$', contacts.views.contactsview, name='contacts'), # View url    
]

The single line in urlpatterns defines the url for one of our Views.

4.5 Create a View

As before, we need to edit our contacts App’s views.py file to create our View.

Edit CODE_HOME/mysite/contacts/views.py.
Add lines under ‘# Create your views here.’, as shown.

from django.shortcuts import render

# Create your views here.

from django.http import HttpResponse

def contactsview(request):
    return HttpResponse("Hello world! from contacts")

4.6 Test our View

If your server isn’t running already.

> cd CODE_HOME/mysite
> python manage.py runserver

In your browser, navigate to http://127.0.0.1:8000/contacts/.
You should see:
Hello world! from contacts

4.7 Add a Model

Django Models are Classes that define database Tables.

We’re going to use the default (==SQLite) settings for our database. If you want to use a different db see Database setup.

Edit the existing file CODE_HOME/mysite/contacts/models.py

Add lines under ‘# Create your models here.’, as shown.

from django.db import models

# Create your models here.

class Contact(models.Model):
    first_name = models.CharField(max_length=255, )
    last_name = models.CharField(max_length=255, )
    email = models.EmailField()

    def __str__(self):
        return ' '.join([
            self.first_name,
            self.last_name,
            self.email,
        ])

    class Meta:
        app_label = 'contacts'

4.8 Run makemigrations

> cd CODE_HOME/mysite
> python manage.py makemigrations contacts

Output will look like this.

Migrations for 'contacts':
  0001_initial.py:
    - Create model Contact

We just created Migrations from our Models for our ‘contacts’ app.

4.9 Run migrate

Now, run migrate to use the Migrations to create the database Tables.

> cd CODE_HOME/mysite
> python manage.py migrate

Output will look similar to this.

Operations to perform:
  Apply all migrations: auth, contacts, admin, sessions, contenttypes
Running migrations:
  Rendering model states... DONE
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying contacts.0001_initial... OK
  Applying sessions.0001_initial... OK

4.10 Load the db using Admin

Now we’ll load the db with some Contacts using the Admin interface.
First, we have to tell Admin about our Contacts Model.

4.10.1 Register Model for Admin

Edit the existing file CODE_HOME/mysite/contacts/admin.py
Add lines under ‘# Register your models here.’, as shown.

from django.contrib import admin

# Register your models here.
from .models import Contact

admin.site.register(Contact)

Now our model will appear in our Admin interface.

4.10.2 Load the db

If your server isn’t running already.

> cd CODE_HOME/mysite
> python manage.py runserver

In your browser, navigate to http://127.0.0.1:8000/admin/.
Enter the Username & Password you entered when you ran createsuperuser. See above.

You should see a nicely formatted Django Admin webpage for your site. It should show something like the following.

Django administration …
Site administration …
Authentication and Authorization …
Contacts
Contacts +Add /Change

Click the +Add button and you should see the following representation of the Fields in our Contact model.

Add contact
First name:
Last name:
Email:

Fill in these three. We’ll use these for testing later.
– Joe Codeswell j@j.com
– Fred Belitnikoff fb@freddy.com
– Jayne Mansfield j@thebomb.com

4.11 List our existing contacts

We need to create a ListView for our contacts.
Then we need to create a Template for our ListView.
Then we need to hook up that view with our URLs.

4.11.1 Create a ListView

Let’s edit our contacts App’s views.py file to add our ListView.

Edit CODE_HOME/mysite/contacts/views.py.
Add lines under return HttpResponse("Hello world! from contacts"), as shown.

...

def contactsview(request):
    return HttpResponse("Hello world! from contacts")

# The new stuff is below here    
from django.views.generic import ListView
from contacts.models import Contact

class ListContactView(ListView):
    model = Contact
    template_name = 'contacts/contact_list.html'

4.11.2 Create a ListView Template

This will be the first Template we have created, since our Hello App didn’t use one.

4.11.2.1 Define Site Template Locations

First we need to determine where we will put the Templates for our site.
This is the subject of much discussion.
The following is my understanding of BEST PRACTICES for Django 1.9.
Edit CODE_HOME/mysite/mysite/settings.py.
– replace 'DIRS': [],
– with 'DIRS': [ *[os.path.join(BASE_DIR, os.path.join('templates', dir)) for dir in site_apps], ]

...

TEMPLATES = [
    {
    ...
        #'DIRS': [],
        'DIRS': [ 
                os.path.join(BASE_DIR, 'templates'),  
        ],

    ...

The BEST PRACTICES effects are:
1. To have all Templates for a Site under a SINGLE ROOT.
2. To keep App Templates separated under individual folders for NAMESPACE purposes.

4.11.2.2 Define our ListView Template

Create the following directory structure under CODE_HOME/mysite.

CODE_HOME
+---mysite
    +---templates
        +---contacts
        +---hello

Create the contact_list.html Template file in CODE_HOME/mysite/templates/contacts/ as follows.

<!-- CODE_HOME/mysite/templates/contacts/contact_list.html -->
<h1>Contacts</h1>

<ul>
  {% for contact in object_list %}
    <li class="contact">{{ contact }}</li>
  {% endfor %}
</ul>

4.11.3 Hook Up our ListView URL

Edit the existing file CODE_HOME/mysite/contacts/urls.py
Add the line shown to the urlpatterns List.

# CODE_HOME/mysite/contacts/urls.py

urlpatterns = [

    # add the following line
    url(r'^listview/$', contacts.views.ListContactView.as_view(), name='listview',),
    ...
]

4.11.4 Test our ListView URL

  1. If the server is NOT ALREADY running,
> cd CODE_HOME/mysite
> python manage.py runserver
  1. Browse to http://127.0.0.1:8000/contacts/listview/
  2. Expected Output is SOMETHING LIKE
Contacts

    Joe Codeswell j@j.com
    Fred Belitnikoff fb@freddy.com
    Jayne Mansfield j@thebomb.com

4.12 CRUD Contacts

CRUD Create, Read, Update and Delete are the primary database operations to be performed on our the elements in our Contacts model.
The Django Views that support CRUD functionality are called Generic Editing Views.
These Views support Making Queries to the DB.

So we can choose among the CRUD functions, we need to make a ListView enabling CRUD for each of our Contacts.

4.12.1 Make a CRUD List View

Edit CODE_HOME/mysite/contacts/views.py.
Add lines under our ListContactView definition as shown.

...

class ListContactView(ListView):
    model = Contact
    template_name = 'contacts/contact_list.html'

# new lines under here
class CRUDListView(ListView):
    model = Contact
    template_name = 'contacts/crudlist.html'

4.12.2 Make a CRUD List Template

Create the crudlist.html Template file in CODE_HOME/mysite/templates/contacts/ as follows.

<!-- CODE_HOME/mysite/templates/contacts/crudlist.html -->
<h1>Contacts</h1>
<a href="/contacts/create">Create</a>
<ul>
  {% for contact in object_list %}
    <li class="contact">{{ contact }} 
      <a href="/contacts/read/{{contact.id}}">Read | </a>
      <a href="/contacts/update/{{contact.id}}">Update | </a>
      <a href="/contacts/delete/{{contact.id}}">Delete</a>  
    </li>
  {% endfor %}
</ul>



4.12.3 Hook up the CRUD List URL

Edit the existing file CODE_HOME/mysite/contacts/urls.py
Add the line shown to the urlpatterns List.

# CODE_HOME/mysite/contacts/urls.py

urlpatterns = [

    # add the following line
    url(r'^crudlist/$', contacts.views.CRUDListView.as_view(), name='crudlist',),
    ...
]

4.12.4 Test CRUD List View

Navigate to http://127.0.0.1:8000/contacts/crudlist/
Expected result looks like the following.

Contacts
Create

    Joe Codeswell j@j.com Read | Update | Delete
    Fred Belitnikoff fb@freddy.com Read | Update | Delete
    Jayne Mansfield j@thebomb.com Read | Update | Delete

4.12.5 Make CRUD Create Contact

Let’s make & test our Create Contact — View, Template & URL Pattern.

View: Edit CODE_HOME/mysite/contacts/views.py.
Add lines under our existing CRUDListView definition as shown.

...

class CRUDListView(ListView):
    model = Contact
    template_name = 'contacts/crudlist.html'

# new lines under here
from django.core.urlresolvers import reverse
from django.views.generic import CreateView
class CreateContactView(CreateView):
    """ Inherits from GENERIC VIEW == CreateView """
    model = Contact
    template_name = 'contacts/create_contact.html'
    fields = ['first_name', 'last_name', 'email']

    def get_success_url(self):
        return reverse('contacts:list') #returns the URL of the Named URL, 'contacts list'

Template: Make the create_contact.html Template file in CODE_HOME/mysite/templates/contacts/ as follows.

<!-- CODE_HOME/mysite/templates/contacts/create_contact.html -->
<h1>Create Contact</h1>

<form action="{% url "contacts:create" %}" method="POST">
  {% csrf_token %}
  <ul>
    {{ form.as_ul }}
  </ul>
  <input id="save_contact" type="submit" value="Save" />
</form>

<a href="{% url "contacts:crudlist" %}">Back to CRUD list.</a>

URL Pattern: Add our Create Contact Url Pattern to CODE_HOME/mysite/contacts/urls.py as follows.

# CODE_HOME/mysite/contacts/urls.py
urlpatterns = [
    ...

    # add the following line
    url(r'^create/$', contacts.views.CreateContactView.as_view(), name='create',),

]

Test:
Navigate to http://127.0.0.1:8000/contacts/create/
Expected Result

Create Contact

    First name:
    Last name:
    Email:

4.12.6 Make CRUD Read Contact

Let’s make & test our Read Contact — View, Template & URL Pattern.
N.B. This Action works on a particular Contact selected by using our CRUD List View.
*View:** Edit CODE_HOME/mysite/contacts/views.py adding code after our CreateContactView class definition.

...
class CreateContactView(CreateView):
   ,,,
        return reverse('contacts:crudlist') #returns the URL of the Named URL, 'contacts list'

# added code under here
from django.views.generic import DetailView        
class ReadContactView(DetailView):
    model = Contact
    template_name = 'contacts/read_contact.html'        

Template: Make the read_contact.html Template file in CODE_HOME/mysite/templates/contacts/ as follows.

<!-- CODE_HOME/mysite/templates/contacts/read_contact.html -->

<h1>CRUD Read Detail</h1>

<p>ID: {{ object.id }}</p>
<p>Last Name: {{ object.last_name }}</p>
<p>First Name: {{ object.first_name }}</p>
<p>Email: {{ object.email }}</p>

URL Pattern: Add our Read Contact Url Pattern to CODE_HOME/mysite/contacts/urls.py as follows.

urlpatterns = [
    ...

    # add the following line
    url(r'^read/(?P<pk>[0-9]+)/$', contacts.views.ReadContactView.as_view(), name='read'),    
    # N.B. Note the use of the Regex Named Group, <pk>, defined special in Django to mean Primary Key.
...
]

N.B. See the note under the inserted url in the above code regarding the use of the Regex Named Group. We don’t even have to mention it Primary Key in our DetailView and Django uses it to Querry the DB for the matching item.

Test 1:
Navigate to http://127.0.0.1:8000/contacts/read/1/
Expected Result

CRUD Read Detail

ID: 1

Last Name: Codeswell

First Name: Joe

Email: j@j.com

Test 2:
Navigate to http://127.0.0.1:8010/contacts/crudlist/
Click on the Read link for the second item (“Fred Belitnikoff fb@freddy.com”)
Expected Result (Same Thing)

CRUD Read Detail

ID: 2

Last Name: Belitnikoff

First Name: Fred

Email: fb@freddy.com

4.12.7 Make CRUD Update Contact

Let’s make & test our Update Contact — View, Template & URL Pattern.
N.B. As with CRUD Read, this Action works on a particular Contact selected by using our CRUD List View.
View: Edit CODE_HOME/mysite/contacts/views.py adding code after our ReadContactView class definition.

...
class ReadContactView(DetailView):
    model = Contact
    template_name = 'contacts/read_contact.html'

# added code under here
from django.views.generic.edit import UpdateView
class UpdateContactView(UpdateView):
    model = Contact
    template_name = "contacts/update_contact.html"
    fields = ['last_name', 'first_name', 'email']
    success_url = 'contacts/crudlist/'

Template: Make the Update Form in the update_contact.html Template file in CODE_HOME/mysite/templates/contacts/ as follows.

<!-- CODE_HOME/mysite/templates/contacts/update_contact.html -->

<h1>Update Contact</h1>

<form action="" method="post">{% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="Update" />
    <button type="reset" value="Reset">Reset</button>
</form>
<button onclick="location.href='/contacts/crudlist'">Cancel</button>

URL Pattern: Add our Update Contact Url Pattern to CODE_HOME/mysite/contacts/urls.py as follows.

urlpatterns = [
    ...

    # add the following line
    url(r'^update/(?P<pk>[0-9]+)/$', contacts.views.UpdateContactView.as_view(), name='updatecontactview'),

    ...
]

Test 1:
A. Navigate to http://127.0.0.1:8000/contacts/update/1/
B. Change First name: Joe to Joseph
C. Press Update
Expected Result

  1. CRUD List is displayed.
  2. Joe Codeswell has been changed to Joseph Codeswell

Test 2:
A. Navigate to http://127.0.0.1:8000/contacts/crudlist/
B. Click on the Update link for Joseph Codeswell
C. Change First name: Joseph to Joe
D. Press Update
Expected Result

  1. CRUD List is displayed.
  2. Joseph Codeswell has been changed BACK to Joe Codeswell

Test 3:
A. Navigate to http://127.0.0.1:8000/contacts/crudlist/
B. Click on the Update link for Joseph Codeswell
C. Change First name: Joe to Joseph
D. Press Reset
Expected Result

  1. Update Contact is reset.
  2. Joseph has been RESET to Joe on the Update Contact form.

Test 4:
A. Navigate to http://127.0.0.1:8000/contacts/crudlist/
B. Click on the Update link for Joseph Codeswell
C. Change First name: Joe to Joseph
D. Press Cancel
Expected Result

  1. CRUD List is displayed.
  2. No change was made. Joe Codeswell STILL REMAINS LISTED as it was.

4.12.8 Make CRUD Delete Contact

Let’s make & test our Delete Contact — View, Template & URL Pattern.
Again, as with CRUD Read & Update, this Action works on a particular Contact selected by using our CRUD List View.
View: Edit CODE_HOME/mysite/contacts/views.py adding code after our UpdateContactView class definition.

...
class UpdateContactView(UpdateView):
   ...
# added code under here
from django.views.generic import DeleteView
class DeleteContactView(DeleteView):
    model = Contact
    template_name = "contacts/delete_contact.html"
    success_url = '/contacts/crudlist/'

Template: Make the Delete Form in the delete_contact.html Template file in CODE_HOME/mysite/templates/contacts/ as follows.

<!-- CODE_HOME/mysite/templates/contacts/delete_contact.html -->

<h1>Delete Contact</h1>

<form action="" method="post">{% csrf_token %}
    <p>Are you sure you want to delete ID: {{ object.id }}, "{{ object }}"?</p>
    <input type="submit" value="Confirm" />
</form>
<button onclick="location.href='/contacts/crudlist'">Cancel</button>

URL Pattern: Add our Delete Contact Url Pattern to CODE_HOME/mysite/contacts/urls.py as follows.

urlpatterns = [
    ...

    # add the following line
    url(r'^delete/(?P<pk>[0-9]+)/$', contacts.views.DeleteContactView.as_view(), name='deletecontactview'),

    ...
]

Test 1:
A. Navigate to http://127.0.0.1:8000/contacts/delete/1/
B. Press Cancel
Expected Result

  1. CRUD List is displayed.
  2. Joe Codeswell is STILL listed.

Test 2:
A. Navigate to http://127.0.0.1:8000/contacts/crudlist/
B. Click on the Delete link for Fred Belitnikoff
C. Press Confirm
Expected Result

  1. CRUD List is displayed.
  2. Fred Belitnikoff has been DELETED.

4.13 Search Contacts

We’ll search our Contacts with 2 types of searches:

  1. A Simple Search == SS (on first_name)
  2. A Parametric Search == PS

We’ll do one of the above for each type of Contact ListView == (ListContactView, CRUDListView) lets make

Yielding 4 Class Based Views:

  1. SimpleSearchListContactView(ListView)
  2. ParametricSearchListContactView(ListView)
  3. SimpleSearchCRUDListView(ListView)
  4. ParametricSearchCRUDListView(ListView)

4.13.1 Simple Search – ListContactView

In the Django Docs see:

  1. “Dynamic filtering”
  2. “Field lookups”

Let’s make & test our – SimpleSearchListContact — View, Template & URL Pattern.
View: Edit CODE_HOME/mysite/contacts/views.py adding code after our DeleteContactView class definition.

...
class DeleteContactView(DeleteView):
   ...
# added code under here
class SimpleSearchListContactView(ListView):

    model = Contact
    template_name = 'contacts/contact_list.html'    
    def get_queryset(self):
        qs = super(SimpleSearchListContactView, self).get_queryset()

        return qs.filter(first_name__contains=self.kwargs['fname'])
        # "first_name" is the first_name field in our Contacts model
        # "contains" is one of Django's "Field lookups"
        # "'fname'" is passed in from our url <fname> Regex Named Group  

Template: We reuse the Template from the ListContactView, contacts/contact_list.html. Nothing else to do here.
URL Pattern: Add our Simple Search ListContactView Url Pattern to CODE_HOME/mysite/contacts/urls.py as follows.

urlpatterns = [
    ...

    # add the following line
    url(r'^ssearch/(?P<fname>[\w-]+)/$', contacts.views.SimpleSearchListContactView.as_view(), name='simplesearchlistcontactview', ),
    # N.B. Note the use of the Regex Named Group, <fname>, sent to the SimpleSearchListContactView as a (name, value) pair used by self.kwargs.
    ...
]

Test 1:
Navigate to http://127.0.0.1:8000/contacts/ssearch/J/
Expected Result
Contact List is displayed showing:

Contacts
    Joe Codeswell j@j.com
    Jayne Mansfield j@thebomb.com

Test 2:
Navigate to http://127.0.0.1:8000/contacts/ssearch/a/
Expected Result
Contact List is displayed showing:

Contacts
    Jayne Mansfield j@thebomb.com

Test 3:
Navigate to http://127.0.0.1:8000/contacts/ssearch/o/
Expected Result
Contact List is displayed showing:

Contacts
    Joe Codeswell j@j.com

4.13.2 Parametric Search – ListContactView

Let’s make & test our – ParametricSearchListContact — View, Template & URL Pattern.
View: Edit CODE_HOME/mysite/contacts/views.py adding code after our SimpleSearchListContactView class definition.

...
class SimpleSearchListContactView(ListView):
   ...
# added code under here
class ParametricSearchListContactView(ListView):
    model = Contact
    template_name = 'contacts/contact_list.html'

    def get_queryset(self):
        qs = super(ParametricSearchListContactView, self).get_queryset()
        field_lookups_dict = {
            '%s__%s'%(self.kwargs['field_name'], self.kwargs['match_cmd']):self.kwargs['value'],
            # for a def of each match_cmd see Field lookups - https://docs.djangoproject.com/en/1.9/ref/models/querysets/#field-lookups
        }
        return qs.filter(**field_lookups_dict)

Template: We reuse the Template from the ListContactView, contacts/contact_list.html. Nothing else to do here.
URL Pattern: Add our Parametric Search ListContactView Url Pattern to CODE_HOME/mysite/contacts/urls.py as follows.

urlpatterns = [
    ...

    # add the following line
    url(r'^psearch/(?P<field_name>[\w-]+)/(?P<match_cmd>[\w-]+)/(?P<value>[\w-]+)/$', contacts.views.ParametricSearchListContactView.as_view(), name='parametricsearchlistcontactview', ),

    ...
]

Test 1:
Navigate to http://127.0.0.1:8000/contacts/psearch/first_name/contains/J/
Expected Result
Contact List is displayed showing:

Contacts
    Joe Codeswell j@j.com
    Jayne Mansfield j@thebomb.com

Test 2:
Navigate to http://127.0.0.1:8000/contacts/psearch/email/contains/bomb/
Expected Result
Contact List is displayed showing:

Contacts
    Jayne Mansfield j@thebomb.com

Test 3:
Navigate to http://127.0.0.1:8000/contacts/psearch/last_name/endswith/eld/
Expected Result
Contact List is displayed showing:

Contacts
    Jayne Mansfield j@thebomb.com

Test 4:
Navigate to http://127.0.0.1:8000/contacts/psearch/last_name/exact/Codeswell/
Expected Result
Contact List is displayed showing:

Contacts
    Joe Codeswell j@j.com

Test 5:
Navigate to http://127.0.0.1:8000/contacts/psearch/last_name/startswith/Man/
Expected Result
Contact List is displayed showing:

Contacts
    Jayne Mansfield j@thebomb.com

Test 6:
Navigate to http://127.0.0.1:8000/contacts/psearch/email/regex/bo/
Expected Result
Contact List is displayed showing:

Contacts
    Jayne Mansfield j@thebomb.com

Test 7:
Navigate to http://127.0.0.1:8000/contacts/psearch/last_name/regex/ode/
Expected Result
Contact List is displayed showing:

Contacts
    Joe Codeswell j@j.com

In summary, see the Django Docs “Field lookups” for a full list of match_cmds like
contains
endswith
exact
startswith
reges
– etc.

4.13.3 Searches (Both Simple & Parametric) for CRUD List View

The CRUD ListView Simple & Parametric Searches are PRETTY MUCH THE SAME AS those for the NON-CRUD ListView.
There are ONLY 2 differences:

  1. The template_name = ‘contacts/crudlist.html’ NOT ‘contacts/contact_list.html’.
  2. The class names – these occur in 2 places in each view.

Let’s make ** & **test BOTH our – SimpleSearchCRUDListContactView & ParametricSearchCRUDListContactView — Views, Templates & URL Patterns AT THE SAME TIME.
View: Edit CODE_HOME/mysite/contacts/views.py adding code for BOTH after our ParametricSearchListContactView class definition.

...
class ParametricSearchListContactView(ListView):
   ...
# added code under here (2 classes for Simple & Parametric Search CRUD ListViews)
class SimpleSearchCRUDListContactView(ListView):

    model = Contact
    template_name = 'contacts/crudlist.html'    

    def get_queryset(self):
        qs = super(SimpleSearchCRUDListContactView, self).get_queryset()

        return qs.filter(first_name__contains=self.kwargs['fname'])
        # "first_name" is the first_name field in our Contacts model
        # "contains" is one of Django's "Field lookups"
        # "'fname'" is passed in from our url <fname> Regex Named Group  


class ParametricSearchCRUDListContactView(ListView):
    model = Contact
    template_name = 'contacts/crudlist.html'


    def get_queryset(self):
        qs = super(ParametricSearchCRUDListContactView, self).get_queryset()
        field_lookups_dict = {
            '%s__%s'%(self.kwargs['field_name'], self.kwargs['match_cmd']):self.kwargs['value'],
            # for a def of each match_cmd see Field lookups - https://docs.djangoproject.com/en/1.9/ref/models/querysets/#field-lookups
        }

        return qs.filter(**field_lookups_dict)

N.B. These classes are the same as the previous 2 EXCEPT:

  1. template_name = ‘contacts/crudlist.html’ NOT ‘contacts/contact_list.html’
  2. Class names are new:
    • Lines 5 & 10 “SimpleSearchCRUDListContactView”
    • Lines 18 & 23 “ParametricSearchCRUDListContactView”

Template: We reuse the Template from the CRUDListView, contacts/crudlist.html. Nothing else to do here.
URL Patterns: Add BOTH our Simple & Parametric Search CRUDListView Url Patterns to CODE_HOME/mysite/contacts/urls.py as follows.

urlpatterns = [
    ...

    # add the following 2 lines
    url(r'^ssearchc/(?P<fname>[\w-]+)/$', contacts.views.SimpleSearchCRUDListContactView.as_view(template_name='contacts/crudlist.htm'), name='ssearchc', ),
    url(r'^psearchc/(?P<field_name>[\w-]+)/(?P<match_cmd>[\w-]+)/(?P<value>[\w-]+)/$', contacts.views.ParametricSearchCRUDListContactView.as_view(template_name='contacts/crudlist.htm'), name='psearchc', ),

    ...
]

Test our CRUD Searches
Perform the “same”

  • 3 tests we did for Simple Searches
  • 7 tests we did for Parametrics Searches
    EXCEPT

  • Navigate to …/contacts/ssearchc/,,, NOT …/contacts/ssearch/… AND

  • Navigate to …/contacts/psearchc/,,, NOT …/contacts/psearch/…

EXPECTED RESULTS
Same as before
EXCEPT items are listed with read, update & delete links.

Content

Click here to go back to Abstract (i.e. Top of Document)

1. Prerequisites
– 1.1 Brief basics of urls and Views
2. Create a Project i.e. a Site
– 2.1 Run django-admin startproject
– 2.2 Verify the Site
3. Hello App
– 3.1 Run django-admin startapp
– 3.2 Add hello to settings.py
– 3.3 Add an App url
– 3.4 Create our App’s urls.py file
– 3.5 Create a View
– 3.6 Test our View
4. Contacts App
– 4.1 Run django-admin startapp contacts
– 4.2 Add contacts to settings.py like for hello
– 4.3 Add an App url like for hello
– 4.4 Create our App’s urls.py file
– 4.5 Create a View
– 4.6 Test our View
– 4.7 Add a Model
– 4.8 Run makemigrations
– 4.9 Run migrate
– 4.10 Load the db using Admin
—- 4.10.1 Register Model for Admin
—- 4.10.2 Load the db
– 4.11 List our existing contacts
—- 4.11.1 Create a ListView
—- 4.11.2 Create a ListView Template
—— 4.11.2.1 Define Site Template Locations
—— 4.11.2.2 Define our ListView Template
—- 4.11.3 Hook Up our ListView URL
—- 4.11.4 Test our ListView URL
– 4.12 CRUD Contacts
—- 4.12.1 Make a CRUD List View
—- 4.12.2 Make a CRUD List Template
—- 4.12.3 Hook up the CRUD List URL
—- 4.12.4 Test CRUD List View
—- 4.12.5 Make CRUD Create Contact
—- 4.12.6 Make CRUD Read Contact
—- 4.12.7 Make CRUD Update Contact
—- 4.12.8 Make CRUD Delete Contact
– 4.13 Search Contacts
—- 4.13.1 Simple Search – ListContactView
—- 4.13.2 Parametric Search – ListContactView
—- 4.13.3 Searches (Both Simple & Parametric) for CRUD List View

A Great Seed for Angular Material – Cordova Apps

A Great App Seed from Mario Aleo

Mario Aleo has published a GREAT App Seed for Cordova – Angular Material Apps on his GitHub Repository cordova-angular-angularMaterial-seed.

When i first encountered a reference to the seed here on stackOverflow, I said “This looks like EXACTLY what i need. It [the Repository Documentation] is VERY WELL EXPLAINED”.

Since then, in the space of just a few hours, I have used Mario’s seed to produce a running app on my tablet. Not only is the GitHub Repository documentation GREAT, but the code in the Mario’s seed is SUPERBLY READABLE. I will learn a lot from using your code, Mario. Thanks.

Also, Mario says that his work is based on AngularJS Best Practices: Directory Structure by Adnan Kukic (@kukicadnan).

Thanks to BOTH of you.

Love and peace,

Joe

Execute Shell Commands from a Python GUI App

GUI Execute Commands

Here’s my Python – TKinter App that takes a set of Shell Commands from a .json file and displays them for individual execution when a button is clicked. Write these frequently used commands once and let the GUI program remember their syntax.

Keep 3 files in the same directory:

  • guiExecuteCommands.py The TKInter Program
  • config.json The Configuration Data
  • cmds.json The Initial Command Set

Contents

  • The TKInter Program File – guiExecuteCommands.py
  • The Configuration Data File – config.json
  • The Initial Command Set – cmds.json
  • A Screenshot of the TKInter Program
  • A run log of the program

The TKInter Program File – guiExecuteCommands.py

Here is the guiExecuteCommands.py file.

#!/usr/bin/python
# -*- coding: utf-8 -*-
# guiExecuteCommands.py
import sys, os, time, subprocess, json, atexit

# if not('.' in sys.path): sys.path.append('.')
# from MyCommands import cmds

import Tkinter, tkFileDialog

class Theapp(Tkinter.Tk):
    def __init__(self,parent):

        # joe's init
        with open('config.json') as data_file:
            cfg_data = json.load(data_file)
        self.cfg_data = cfg_data 

        with open(cfg_data[0]['commandsf']) as data_file:
            cmds = json.load(data_file)
        self.cmds = cmds 


        atexit.register(self.on_exit) # http://stackoverflow.com/a/28664350/601770


        # /joe's init




        Tkinter.Tk.__init__(self,parent)
        self.parent = parent
        self.initialize()

    def on_exit(self):
        ''' http://stackoverflow.com/a/28664350/601770  
            save the state that has been changed
        '''

        self.cfg_data[0]['workingd'] = self.cwd.get()


        print "self.cwd.get(): %s"%(self.cwd.get())
        print "self.cfg_data: %s"%(self.cfg_data)

        with open('config.json', 'w') as f:
            json.dump(self.cfg_data, f)

    def execute_cmd(self, rnum):
        ''' C:\1d\PythonPjs\subprocessPjs\executeCommandsPj\executeCommands\executeCommands.py

        '''
        commandstring = self.cmd_entries[rnum].get()
        cwdpath = self.cwd.get()
        outstr = subprocess.check_call(commandstring, cwd=cwdpath, shell=True)  #  Convenience Function
        print outstr


    ## http://stackoverflow.com/a/16777523/601770   
    def bclick(self, rnum):
        def click():            
            self.execute_cmd(rnum)
        return click



    def browse2cwd(self):   
        """
        http://stackoverflow.com/questions/11295917/how-to-select-a-directory-and-store-the-location-using-tkinter-in-python
        http://tkinter.unpythonic.net/wiki/tkFileDialog
            find "def askdirectory(self):"
        """   
        tkFileDiaOpt4Dir = {}
        tkFileDiaOpt4Dir['initialdir'] = self.cwd.get()
        tkFileDiaOpt4Dir['mustexist'] = False
        tkFileDiaOpt4Dir['parent'] = self
        tkFileDiaOpt4Dir['title'] = 'Browse to set CWD.'

        self.cwd.set( tkFileDialog.askdirectory(**tkFileDiaOpt4Dir) )

        print "browse2cwd:  self.cwd.get(): %s"%(self.cwd.get())

    def initialize(self):
        self.cwd = Tkinter.StringVar()
        self.cwd.set(self.cfg_data[0]['workingd'])

        self.grid()  # make this be a grid

        ### add the gui elements in here

        ## browse to working directory
        label = Tkinter.Label(self, text="Working Directory", fg="white",bg="blue")
        label.grid(column=0,row=0,columnspan=3,sticky='W')

        self.cwd_entry = Tkinter.Entry(self, width=80, textvariable=self.cwd)
        self.cwd_entry.grid(column=0,row=1,columnspan=2, sticky='W') # http://effbot.org/tkinterbook/entry.htm

        b = Tkinter.Button(self,text=u"Browse", command=self.browse2cwd)
        b.grid(column=2,row=1) 

        toprowscount = 2


        ## command row elements len == len(MyCommands.cmds)
        self.buttons = []
        self.cmd_entries = []
        for rnum, cmd in enumerate(self.cmds):


            label = Tkinter.Label(self, anchor="w",text=cmd['label'], fg="white",bg="blue")
            label.grid(column=0,row=rnum+toprowscount,sticky='EW')

            self.cmd_entries.append( Tkinter.Entry(self, width=60) )
            self.cmd_entries[-1].grid(column=1,row=rnum+toprowscount,sticky='EW'); self.cmd_entries[-1].insert(0, (cmd['cmd']+' '+ cmd['args']))


            b = Tkinter.Button(self,text=u"Ex%s"%(rnum), command=self.bclick(rnum))
            b.grid(column=2,row=rnum+toprowscount) 

            self.buttons.append(b)


        ## Finish up
        self.grid_columnconfigure(0,weight=1) # make resizable
        self.resizable(True,False)            # make resizable ONLY HORIZONTALLY




if __name__ == "__main__":
    app = Theapp(None)
    app.title('Gui Execute Comands')
    app.mainloop()

The Configuration Data File – config.json

Here is the config.json file.

[{
   "programd": "C:\\1d\\PythonPjs\\GUIpjs\\guiExecuteCommandPj\\guiExecuteCommand", 
   "commandsf": "C:\\1d\\PythonPjs\\GUIpjs\\guiExecuteCommandPj\\guiExecuteCommand\\cmds.json",  
   "configd": "C:\\1d\\PythonPjs\\GUIpjs\\guiExecuteCommandPj\\guiExecuteCommand", 
   "workingd": "C:/1d/CrossPlatformPjs/CordovaPjs/cordovaAngular00Pj/cordovaAngular00"
}]

The Initial Command Set – cmds.json

Here is the (INITIAL) cmds.json file

[
  {"cmd": "cordova", "args": "run android", "label": "android build 4 device"}, 
  {"cmd": "cordova", "args": "emulate android", "label": "android build 4 emulation"}, 
  {"cmd": "dir", "args": "*", "label": "dir of working dir"}
]  

A Screenshot of the TKInter Program

Here is a screenshot of the program.

guiExecuteShot

A run log of the program

Here is a run log.

Note: if the user changed the working directory, on exit the new working directory will be saved in the config.json file.

C:\1bat\guiExecuteCommands>guiExecuteCommands.py
browse2cwd:  self.cwd.get(): C:/1d/CrossPlatformPjs/Building Single Page Web Apps with AngularJS PJ
 Volume in drive C is Windows
 Volume Serial Number is F2FB-4810

 Directory of C:\1d\CrossPlatformPjs\Building Single Page Web Apps with AngularJS PJ

12/23/2015  09:58 AM    <DIR>          .
12/23/2015  09:58 AM    <DIR>          ..
12/23/2015  11:11 AM    <DIR>          zetc
               0 File(s)              0 bytes
               3 Dir(s)  900,352,217,088 bytes free
0
self.cwd.get(): C:/1d/CrossPlatformPjs/Building Single Page Web Apps with AngularJS PJ
self.cfg_data: [{u'programd': u'C:\\1d\\PythonPjs\\GUIpjs\\guiExecuteCommandPj\\guiExecuteCommand', u'commandsf': u'C:\\1d\\PythonPjs\\GUIpjs\\guiExecuteCommandPj\\guiExecuteCommand\\cmds.json', u'configd': u'C:\\1d\\PythonPjs\\GUIpjs\\guiExecuteCommandPj\\guiExecuteCommand', u'workingd': u'C:/1d/CrossPlatformPjs/Building Single Page Web Apps with AngularJS PJ'}]

C:\1bat\guiExecuteCommands>

Show jQuery Client info in web2py View

This web2py View shows jQuery Client Side Info

{{left_sidebar_enabled,right_sidebar_enabled=False,('message' in globals())}}
{{extend 'layout.html'}}

{{block head}}

        $(document).ready(function(){
            $("#width").html($(window).width());
            $("#height").html($(window).height());

        });

{{end}}

{{if 'message' in globals():}}
<h3>{{=message + "hello 3"}}</h3>
<h3>width: <span id="width"></span>    height: <span id="height"></span></h3>
{{pass}}