Creating a New Django App - VisionSystemsInc/voxel_globe GitHub Wiki

Creating a New Django App

Here is how to create a new app with a form, with an endpoint to handle the form submission, an html template,

Adding a new app

  1. ./just manage startapp {app_name}

  2. Edit voxel_globe/vip/urls.py, add a new url to the urlpatterns tuple

  3. Add the url entry

    url(r'^apps/{app_name}/', 
        include('voxel_globe.{app_name}.urls', 
        namespace='{app_name}')),
  4. Create and edit voxel_globe/{app_name}/urls.py to contain the new django endpoints, typically

    from django.conf.urls import url
    import voxel_globe.{app_name}.views as views
    
    urlpatterns = [
        url(r'^$', views.{my_page}, name='{my_page_name}'),
    ]
  5. Edit voxel_globe/{app_name}/views.py, typically

from django.shortcuts import render, redirect

def {my_page}(request):
  return render(request, '{app_name}/html/{my_page}.html',
                {'title': 'Voxel Globe - {App Title here}',
                 'page_title': 'Voxel Globe - {App Page Title here}'})
  1. Create and edit voxel_globe/{app_name}/templates/{app_name}/html/{my_page}.html
{% extends "main/common_header.html" %}
{% load staticfiles %}

<!-- Include the client side javascript code -->
{% block includes %}

<style>
</style>

{% endblock %}

{%block globalfunctions %}
// Global functions defined outside of the jquery document ready function can be put here.
{% endblock %}

{% block javascript %}

{% endblock %}

{% block content %}
<div id="mainContainer" class="main-content">

<h1>hi</h1>

</div>
{% endblock %}
  1. Optionally, edit main/templates/main/html/index.html to include a main-page link to first page of the new app. Something, perhaps, like:
    <li><a href="{% url '{app_name}:{view}' %}">Application Name</a></li>
  1. Finally, edit vip/settings.py and add voxel_globe.{app_name} to INSTALLED_APPS
  2. Restart the uwsgi and asgi daemon

Adding Unit Tests

  1. Edit voxel_globe/{app_name}/tests.py to contain the units tests, typically
from django.test import TestCase
from voxel_globe.common_tests import VoxelGlobeTestCase
#optionally
from django.urls import reverse
from voxel_globe.tools.django import partial_reverse


class Vip{appname}TestCase(VoxelGlobeTestCase):
  def setUp(self):
    self.client = self.setupVoxelGlobeTestCase()

  def test_{test_name}(self):
    r = self.client.post(reverse('{appname}:{view_url_name}'), 
                         data='{}', #example json data
                         content_type="application/json")

    r = self.client.post(partial_reverse('{appname}:{view_url_name}')+'{rest_of_partial_url}',
                         data='77', #example data
                         content_type="application/json")

See here for more information on Django unit tests. The test class inherits from unittest.TestCase

Adding a page with a form

Here is an example form just to give you an idea

  1. Create and edit voxel_globe/{app_name}/forms.py to add the form, for example
from django import forms
import voxel_globe.meta.models as models

class {AppName}Form(forms.Form):
  image = forms.ModelChoiceField(label="Height image", 
      queryset=models.Image.objects.filter(name__startswith='Height Map').order_by('name'))
  1. Edit voxel_globe/{app_name}/views.py, typically
def {my_form}(request):
  auto_open = False

  if request.method == 'POST':
    form = {AppName}Form(request.POST)

    if form.is_valid():
      import voxel_globe.{app_name}.tasks as tasks

      image_id = form.data['image']

      task = tasks.{task_name}.apply_async(args=(image_id,))
      auto_open = True
  else:
    form = {AppName}Form()

  return render(request, '{app_name}/html/my_form.html',
                {'title': 'Voxel Globe - {App Title here}',
                 'page_title': 'Voxel Globe - {App Page Title here}',
                 'form':form, 'task_menu_auto_open': auto_open})
  1. Create and edit voxel_globe/{app_name}/templates/{app_name}/html/my_form.html
{% extends "main/common_header.html" %}
{% load staticfiles %}

<!-- Include the client side javascript code -->
{% block includes %}

<style>
</style>

{% endblock %}

{%block globalfunctions %}
// Global functions defined outside of the jquery document ready function can be put here.
{% endblock %}

{% block javascript %}

{% endblock %}

{% block content %}
<div id="mainContainer" class="main-content">
  <form action="{% url '{app_name}:{my_form_name}' %}" method="post">
    {% csrf_token %}
    <table>
    {{ form.as_table }}
    </table>
    <input type="submit" value="Submit" />
  </form>
</div>
{% endblock %}
  1. Add the new url to voxel_globe/{app_name}/urls.py

        url(r'^$', views.{my_form}, name='{my_form_name}'),

Adding a status page

By default, all tasks use voxel_globe/task/templates/task/html/{app_name}_tasks_{task_name}.html for status, and if that does not exist, it defaults to the voxel_globe/task/templates/task/html/default_status.html page. To write a custom status page, create and edit voxel_globe/task/templates/task/html/{app_name}_tasks_{task_name}.html to include

{% extends "main/common_header.html" %}
{% load staticfiles %}

The following blocks can be defined to have a custom task status

  • task_info - The Task info display at the top of the status block
  • failure - Used when task.state == "Failure"
  • revoked - Used when task.state == "Revoked"
  • success - Used when task.state == "Success"
  • task_specific - Used for all other task.states
    • If results dictionary contains the keys index and total, a progress bar is used to show the status
  1. Create and edit voxel_globe/{app_name}/templates/{app_name}/html/order_status.html, for example

    {% extends "task/html/default_status.html" %}
    
    {% block task_info %}
    Task ID: {{ task.task_id }}<BR>
    {% endblock %}
    
    {% block failure %}
      State: {{ task.state }}<BR>
      Reason: {{ task.result }}<BR>
    {% endblock %}
    
    {% block task_specific %}
      {% if task.state == "INITIALIZE" %}
        State: {{ task.state }}<BR>
        Stage: {{ task.result.stage }} {{ task.result.i }}/{{task.result.total }}<BR>
      {% elif task.state == "PRELOADING" %}
        State: {{ task.state }}<BR>
        Stage: {{ task.result.stage }} {{ task.result.i }}/{{task.result.total }}<BR>
      {% elif task.state == "PROCESSING" %}
        State: {{ task.state }}<BR>
        Stage: {{ task.result.stage }} {{ task.result.i }}/{{task.result.total }}<BR>
        {% if task.result.stage == "update" or task.result.stage == "color_update" %}
          Image: {{ task.result.image }}/{{task.result.images }}
        {% endif %}
      {% else %}
        State: {{ task.state }}<BR>
        Result: {{ task.result }}<BR>
      {% endif %}
    {% endblock %}

Adding models for your app

  1. Add any models you will need to the newly created models.py (uncommon)

Adding an entry on the main page

  1. Edit voxel_globe/main/templates/main/html/index.html and add the new url to the main page

    <li><a href="{% url '{app_name}:make_order' %}">Order {App Name}</a></li>

Adding a Processing Task

  1. Create voxel_globe/{app_name}/__init__.py

  2. Create and edit voxel_globe/{app_name}/tasks.py, for example

    import os
    from os import environ as env
    
    from celery.utils.log import get_task_logger
    logger = get_task_logger(__name__)
    import logging
    
    from voxel_globe.common_tasks import shared_task, VipTask
    
    @shared_task(base=VipTask, bind=True)
    def {task_name}(self, voxel_world_id, prob=0.5):
        pass
  3. Finally, edit vip/settings.py and add voxel_globe.{app_name} to INSTALLED_APPS

  4. Restart celery workers

Advance celery routing

It's really easy to route a task to a specific key.

@shared_task(base=VipTask, bind=True, routing_key="gpu")
⚠️ **GitHub.com Fallback** ⚠️