Skip to content

Instantly share code, notes, and snippets.

@gipi
Last active March 9, 2017 23:54
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save gipi/1480541 to your computer and use it in GitHub Desktop.
Save gipi/1480541 to your computer and use it in GitHub Desktop.
#django #south #celery

Cookbook | Packages | Tests | Presentations | Django suit | South | URL reversing and namespace | Logging | Factory | Celery

TRICKS

Models

Multi value field

There a subtle bug for the required property in a MultiValueField

class IndirizzoField(forms.MultiValueField):
    def __init__(self, **kwargs):
        fields = (
            forms.CharField(required=False, initial=''),
            forms.CharField(required=False, initial=''),
            forms.CharField(required=False, initial=''),
            forms.CharField(required=False, initial=''),
            forms.CharField(required=False, initial=''),
            forms.CharField(required=False, initial=''),
        )
        self.was_required =  kwargs.get('required', True)
        kwargs['required'] = False
        super(IndirizzoField, self).__init__(fields, **kwargs)

    def compress(self, data_list):
        if self.was_required and len(data_list) == 0:
            raise forms.ValidationError("Inserisci almeno un valore")

        return '$'.join(data_list)

class IndirizzoWidget(forms.MultiWidget):
    def __init__(self, attrs=None):
        self.count = 6
        self.labels = (
            'Via',
            'N',
            'CAP',
            'Comune',
            'Provincia',
            'Nazione',
        )
        widgets = (
            forms.TextInput(),
        ) * self.count
        super(IndirizzoWidget, self).__init__(widgets, attrs)

    def decompress(self, value):
        if not value:
            return [None, None, None, None, None,]

        return value.split('$')

    def format_output(self, rendered_widgets):
        widgets = []
        for label, widget in zip(self.labels, rendered_widgets):
                 widgets.append(u'<b>%s</b> %s' % (label, widget))
        return ''.join(widgets)

OpenDocument Response

import zipfile
import StringIO

	def _make_ods(self, request, queryset):
		ods_name = os.path.abspath(os.path.dirname(__file__) + '/ods/template.ods')
		if len(queryset) > 1:
			self.message_user(request, "Please choose only one")
			# FIXME: I don't know if is correct to return
			# without a response
			return
		# http://djangosnippets.org/snippets/15/
		"""
		odf_path - path to the template document
		  context - dictionary to create context from
		"""
		ordine = queryset[0]
		# per evitare che mi crei doppioni utilizzo un set() per le destinazioni dei materiali
		depositi = ordine.destinazione
		c = Context({
			"ordine": ordine,
			"depositi": depositi,
		})
		# default mimetype
		mimetype = 'application/vnd.oasis.opendocument.text'
		# ODF is just a zipfile
		input = zipfile.ZipFile(ods_name, "r" )
		# we cannot write directly to HttpResponse, so use StringIO
		text = StringIO.StringIO()
		# output document
		output = zipfile.ZipFile(text, "a")
		# go through the files in source
		for zi in input.filelist:
			out = input.read(zi.filename)
			# waut for the only interesting file
			if zi.filename == 'content.xml':
				# un-escape the quotes (in filters etc.)
				t = Template(out) 
				# render the document
				out = t.render(c) 
			elif zi.filename == 'mimetype':
				# mimetype is stored within the ODF
				mimetype = out 
			output.writestr(zi.filename, smart_str(out))

		# close and finish
		output.close()
		response = HttpResponse(content=text.getvalue(), mimetype=mimetype)

		response['Content-Disposition'] = 'attachment; filename=ordine-acquisto.ods'

		return response

POSTGRES

# aptitude -PV install libpq-dev
(env) $ pip install psycopg2

If you tryied to drop the tables but dependencies errors pops out try this

# drop table app_model cascade;

MYSQL

# apt-get install libmysqlclient-dev
$ pip install MySQL-python

In order to support transactions you need to use InnoDB engine

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'apidb',
        'USER': 'root',
        'PASSWORD': 'password',
        'OPTIONS': {
            'init_command': 'SET storage_engine=INNODB',
        }
    }

WONTFIX

CELERY

Broker redis

Gives the length of the queue

$ redis-cli  -h localhost -p 6379 llen celery

If you want to purge all the queues

$ python app/manage.py celery purge
/var/www/realistico
WARNING: This will remove all tasks from queue: celery.
         There is no undo for this operation!

(to skip this prompt use the -f option)

Are you sure you want to delete all tasks (yes/NO)? yes
No messages purged from 1 queue

Obtain the regex associated with an URL name

    def _reverse(self, name):
        '''Return the pattern associated with a given url name'''
        r = None
        for regex in urlpatterns:
            if regex.name == name:
                r = regex

        return r.regex.pattern

Obtain the Urls defined

In [1]: from core.urls import urlpatterns

In [2]: urlpatterns
Out[2]: 
[<RegexURLResolver <module 'debug_toolbar.toolbar' from '/opt/subasta/.virtualenv/local/lib/python2.7/site-packages/debug_toolbar/toolbar.pyc'> (djdt:djdt) ^__debug__/>,
 <RegexURLPattern index ^$>,
 <RegexURLPattern None ^email-sent/$>,
 <RegexURLResolver <module 'social.apps.django_app.urls' from '/opt/subasta/.virtualenv/src/python-social-auth-master/social/apps/django_app/urls.pyc'> (None:sex) >,
 <RegexURLResolver <module 'django.contrib.auth.urls' from '/opt/subasta/.virtualenv/local/lib/python2.7/site-packages/django/contrib/auth/urls.pyc'> (None:auth) >,
 <RegexURLResolver <RegexURLPattern list> (paymentt:payment) >,
 <RegexURLPattern all_categories deal/category/$>,
 <RegexURLPattern category deal/category/(?P<category>[\w ]*)/$>,
 <RegexURLResolver <module 'deal.urls' from '/opt/subasta/deal/urls.pyc'> (None:deal) ^deal/>,
 <RegexURLPattern require_email ^mail_required/$>,
 <RegexURLResolver <RegexURLPattern list> (admin:admin) ^admin/>,
 <RegexURLPattern profile ^profile/$>,
 <RegexURLPattern privacy ^privacy-policy/$>,
 <RegexURLPattern None ^media\/(?P<path>.*)$>]

Avoid None if value is not defined

{{ obj.attr|default_if_none:"" }}

Readonly field

class TrasportoAdminForm(forms.ModelForm):
    class Meta:
        model = Trasporto
        widgets = {
            'deposito_partenza': forms.widgets.Select(attrs={'readonly': True}),
            'azienda_partenza':  forms.widgets.Select(attrs={'readonly': True}),
        }

Reverse with GET

        azienda_partenza, deposito_partenza = materiale.deposito
        return HttpResponseRedirect('%s?%s' % (
            reverse('admin:trasporto_trasporto_add'),
            urllib.urlencode({
                'azienda_partenza':  azienda_partenza.pk,
                'deposito_partenza': deposito_partenza.pk,
            }),
        ))

Require at least one element in an inline

# http://stackoverflow.com/questions/5648563/django-forcing-admin-users-to-enter-at-least-one-item-in-tabularinline
class AtLeastOneRequiredInlineFormSet(BaseInlineFormSet):
    def clean(self):
        """Check that at least one service has been entered."""
        super(AtLeastOneRequiredInlineFormSet, self).clean()
        if any(self.errors):
            return
        if not any(cleaned_data and not cleaned_data.get('DELETE', False)
            for cleaned_data in self.cleaned_data):
            raise forms.ValidationError('At least one item required.')

class DealImageInlineAdmin(admin.TabularInline):
    model = DealImage
    formset = AtLeastOneRequiredInlineFormSet

Enforce check on empty inlines

class RequiredFormSet(BaseInlineFormSet):
    def __init__(self, *args, **kwargs):
        super(RequiredFormSet, self).__init__(*args, **kwargs)
        for form in self.forms:
            form.empty_permitted = False

class WhateverInlineAdmin(admin.StackedInline):
    model   = Whatever
    formset = RequiredFormset

Action with intermediate page

class WhateverAdmin(admin.ModelAdmin):
    model = Whatever
    actions = [
        'decidi_colli',
    ]

    def get_urls(self):
        from django.conf.urls import patterns, url
        urls = super(MaterialeMagazzinoAdmin, self).get_urls()

        info = self.model._meta.app_label, self.model._meta.model_name

        my_urls = patterns('',
            url(r'^decidi_colli/(?P<pk>\d.*)/$', self.admin_site.admin_view(self.decidi_colli_view), name='%s_%s_decidi_colli' % info),
        )
        return my_urls + urls

    def decidi_colli(self, request, queryset):
        '''This is the usual action that simple check for data and redirect to the intermediate page'''
        if len(queryset) != 1:
            self.message_user(request, 'Devi indicare un solo materiale')
            return

        genitore = queryset[0]

        return HttpResponseRedirect(reverse('admin:decidi_colli', args=[genitore.pk]))


    def decidi_colli_view(self, request, pk):
        '''This is the intermediate page'''

        # do here what you need in your intermediate page
        return render_to_response(
            'admin/magazzino/_decidi_colli.html', {
            }, RequestContext(request))

Disable editing on editing

class WhateverAdmin(admin.ModelAdmin):
    def get_readonly_fields(self, request, obj=None):
        ro_fields = super(WhateverAdmin, self).get_readonly_fields(request, obj)

        if obj:
            ro_fields += ('whatever',)

        return ro_fields

Adding DateTimeField

{% block extrajs %}
{{ super }}
    <script type="text/javascript" src="{% url 'admin:jsi18n' %}"></script>
    <script type="text/javascript" src="{% static 'admin/js/jquery.min.js' %}"></script>
    <script type="text/javascript" src="{% static 'admin/js/jquery.init.js' %}"></script>
    <script type="text/javascript" src="{% static 'admin/js/core.js' %}"></script>
    <!-- media -->
    {{ form.media }}
{% endblock %}

Command

from django.core.management.base import BaseCommand
from utilities import publisher

class Command(BaseCommand):
    help = 'Manually publish'

    def add_arguments(self, parser):
        parser.add_argument('house_ids', nargs='*', type=int, help='house id of the view to be published')

    def handle(self, *args, **options):
            pks = options['house_ids']
            print options, pks
            publisher.process(pks=pks)

Raise exception on undefined variable into template

class InvalidString(str):
    def __mod__(self, other):
        from django.template.base import TemplateSyntaxError
        if other != 'form_control_class':
            print other
            return ''
        raise TemplateSyntaxError(
            "Undefined variable or unknown value for: %s" % other)

TEMPLATE_STRING_IF_INVALID = InvalidString("%s")

TEMPLATES[0]['OPTIONS']['string_if_invalid'] = TEMPLATE_STRING_IF_INVALID

User from session

from django.contrib.sessions.models import Session
from django.contrib.auth.models import User

session_key = '8cae76c505f15432b48c8292a7dd0e54'

session = Session.objects.get(session_key=session_key)
uid = session.get_decoded().get('_auth_user_id')
user = User.objects.get(pk=uid)

print user.username, user.get_full_name(), user.email

System check

# __init__.py
from . import checks
# checks.py
from django.core.checks import register, Warning, Error
from django.conf import settings

@register('aws')
def check_aws_settings(**kwargs):
    errors = []

    if hasattr(settings, 'AWS_S3_FILE_OVERWRITE') and settings.AWS_S3_FILE_OVERWRITE:
        errors.append(
            Error(
                "The project need AWS_S3_FILE_OVERWRITE set to False",
                id='realistico.E001',
            )
        )

    return errors

It's possible to generate random-like instance of models using factory-boy.

from __future__ import absolute_import
import factory
from .models import Whatever
from django.contrib.auth.models import User
from django.core.files.base import ContentFile
from django.contrib.auth import get_user_model

# TODO: cache the result
def get_all_files_from_dir(dir):
    # return list with all the absolute paths of the files contained into a directory
    import os

    absolute_paths = []
    for _, dirs, files in os.walk(dir):
        for file in files:
            if file.endswith('.jpg'): # FIXME
                absolute_paths.append(os.path.join(dir, file))
            else:
                logger.error('file withwrong extension: %s' % file)

    return absolute_paths

def _get_random_image_data():
    import os
    import random
    from django.conf import settings

    dir_image_path = os.path.join(settings.SITE_ROOT, 'temp')

    image_path = random.choice(get_all_files_from_dir(dir_image_path))
    with open(image_path) as image_file:
        return image_file.read()

class UserFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = get_user_model()
        django_get_or_create = ('username',)

    username = factory.Sequence(lambda n: 'User {0}'.format(n))
    # http://stackoverflow.com/questions/15616277/how-can-you-create-an-admin-user-with-factory-boy
    password = factory.PostGenerationMethodCall('set_password', 'password')
    
class WhateverFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = Whatever

    name = factory.sequence(lambda n: 'Whatever {0}'.format(n))
    # https://stackoverflow.com/questions/25806428/how-to-make-factoryboys-imagefield-generate-image-before-save-is-called
    image = factory.LazyAttribute(
            lambda _: ContentFile(_get_random_image_data(), 'example.jpg')
        )

It's important to know how to report information

import logging
logging.basicConfig()
# 'django' is the catch all
logger = logging.getLogger('django')
LOGGING_DEFAULT = {
    'handlers': ['console', 'file'],
    'level': 'DEBUG',
    'propagate': True,
}

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
        },
        'simple': {
            'format': '%(levelname)s %(message)s'
        },
    },
    'handlers': {
        'null': {
            'level': 'DEBUG',
            'class': 'django.utils.log.NullHandler',
        },
        # configure the log to be rotated daily
        'file': {# see https://docs.python.org/2.7/library/logging.handlers.html#logging.handlers.TimedRotatingFileHandler
            'level': 'DEBUG',
            'formatter': 'verbose',
            'class': 'logging.handlers.TimedRotatingFileHandler',
            'filename': build_path_from_root('debug.log'),
            'when':     'midnight',
        },
        'console':{
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        'mail_admins': {
            'level': 'ERROR',
            'class': 'django.utils.log.AdminEmailHandler',
        }
    },
    'loggers': {
        'django.request': {
            'handlers': ['console', 'file', 'mail_admins',],
            'level': 'ERROR',
            'propagate': False,
        },
        'anagrafica': LOGGING_DEFAULT,
        'magazzino':  LOGGING_DEFAULT,
        'trasporto':  LOGGING_DEFAULT,
    }
}

if you want to configure celery's logging then set CELERYD_HIJACK_ROOT_LOGGER to False and add a loggers entry.

# source: http://programming.oreilly.com/2014/04/simplifying-django.html
import sys
from django.conf import settings
from django.conf.urls import patterns
from django.http import HttpResponse
from django.core.management import execute_from_command_line
settings.configure(
DEBUG=True,
SECRET_KEY='placerandomsecretkeyhere',
ROOT_URLCONF=sys.modules[__name__],
)
def index(request):
return HttpResponse('Powered by Django')
urlpatterns = patterns('',
(r'^$', index),
)
if __name__ == "__main__":
execute_from_command_line(sys.argv)
upstream myproject {
server unix:/var/run/myproject-gunicorn.sock fail_timeout=0;
}
server {
access_log /var/log/nginx/myproject.access.log;
error_log /var/log/nginx/myproject.error.log;
server_name myproject.it;
location /admin {
rewrite ^(.*)$ https://myproject.it$1 permanent;
}
location ^/static {
root /var/www/myproject/;
expires 30d;
#access_log off;
}
location / {
proxy_set_header X-Forwarded-For $scheme;
proxy_set_header Host $http_host;
proxy_redirect off;
if (!-f $request_filename) {
proxy_pass http://myproject;
break;
}
}
}
server {
listen 443;
ssl_certificate /etc/ssl/certs/dovecot.pem;
ssl_certificate_key /etc/ssl/private/dovecot.pem;
server_name myproject.it;
location ^/static {
root /var/www/myproject;
expires 30d;
access_log off;
}
location / {
proxy_set_header X-Forwarded-For $scheme;
proxy_set_header Host $http_host;
proxy_redirect off;
if (!-f $request_filename) {
proxy_pass http://myproject;
break;
}
}
}

PACKAGES

# http://www.rkblog.rk.edu.pl/w/p/profiling-django-object-size-and-memory-usage-pympler/
import traceback
import inspect
import io
import operator
from pympler import muppy
from pympler import summary
from pympler import refbrowser
from pympler import tracker
from pympler.asizeof import asizeof
from pympler import refgraph
from pympler import classtracker
g_start_objects = muppy.get_objects()
tr = classtracker.ClassTracker()
def output_function(o):
import ipdb;
#ipdb.set_trace()
if inspect.isframe(o):
traceback.print_stack(o)
pass
return str(type(o))
class memoryMiddleware(object):
"""
Measure memory taken by requested view, and response
"""
def process_request(self, request):
self.memory_profiling = request.GET.has_key('m')
self.graph = request.GET.has_key('g')
self.stats = request.GET.has_key('s')
self.tracing = request.GET.has_key('t')
if self.memory_profiling:
self.start_objects = muppy.get_objects()
def process_response(self, request, response):
print request
if self.memory_profiling:
self.end_objects = muppy.get_objects()
if self.graph:
rf = refgraph.ReferenceGraph(self.end_objects)
rf.render('/tmp/reference_graph.ps')
if self.stats or self.tracing:
sum_start = summary.summarize(self.start_objects)
g_sum_start = summary.summarize(g_start_objects)
sum_end = summary.summarize(self.end_objects)
diff = summary.get_diff(sum_start, sum_end)
g_diff = summary.get_diff(g_sum_start, sum_end)
summary.print_(diff)
summary.print_(g_diff)
if self.tracing:
_io = muppy.filter(self.end_objects, Type=io.BytesIO)
#import ipdb;ipdb.set_trace()
print '~~~~~~~~~'
cb = refbrowser.ConsoleBrowser(_io[0], maxdepth=2, str_func=output_function)
cb.print_tree()
print '~~~~~~~~~'
a = asizeof(response)
print 'Total size of response object in kB: %s' % str(a / 1024.0)
print '~~~~~~~~~'
a = asizeof(self.end_objects)
print 'Total size of end_objects in MB: %s' % str(a / 1048576.0)
b = asizeof(self.start_objects)
print 'Total size of start_objects in MB: %s' % str(b / 1048576.0)
print '~~~~~~~~~'
return response

You have to remember che there are two states for your database, one is the situation in which it's when you generate the migration and the other is when you will run the migration: when you freeze a model you can access it using the orm['appname.modelName'] dictionary.

NB: When you will execute your tests, more migrations equal more times!!!

First of all install it

$ pip install south

and add it to your INSTALLED_APPS settings' variable

INSTALLED_APPS += (
    'south',
)

After a syncdb there are two possibilities: the application has already south's migrations

$ python manage.py schemamigration <app> --initial

or application doesn't have migrations associated

$ python manage.py convert_to_south <app>

In this last case, when the same code will be deployied, in this deployment we have to do

$ python manage.py migrate <app> 0001 --fake

If some errors occur

$ find . -maxdepth 2 -path './*/migrations' -exec rm -vfr \{\} \;
$ python manage.py reset south
$ python manage.py convert_to_south <app>

It's possible to delete the migrations files and pass --delete-ghost-migrations so to remove from south's table the migrations not corresponding to migration files. Otherwise

 $ python manage.py dbshell
 DB> DELETE from south_migrationhistory where app_name='trasporto';

In order to do a migration (is unimportant if forward or backward) simple use the command migrate indicating the identifier of the migration state to which we want to recover.

Note: South has some problems with permissions app but a possible workaround can be this.

It is also possible to indicate dependencies between migrations:

# forum/migrations/0002_post.py
class Migration:
    
    depends_on = (
        ("accounts", "0003_add_user_profile"),
    )

    def forwards(self):
        ....

NB: Also note that the backwards() method gets the ORM as frozen by the previous migration except for migrations that define symmetrical = True (new in South 1.0).

##COOKBOOK

###Renaming

If you want to rename a model (and hence a table), you can create a migration as usual

$ python schemamigration app_name --auto

and the edit

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Deleting model 'Foo'                                                                                                                      
        db.delete_table('myapp_foo')

        # Adding model 'Bar'                                                                                                                        
        db.create_table('myapp_bar', (
        ...
        ))
        db.send_create_signal('myapp', ['Bar'])

    def backwards(self, orm):
        ...

to obtain this

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Renaming model from 'Foo' to 'Bar'                                                                                                                      
        db.rename_table('myapp_foo', 'myapp_bar')                                                                                                                        
        db.send_create_signal('myapp', ['Bar'])

    def backwards(self, orm):
        # Renaming model from 'Bar' to 'Foo'                                                                                                                      
        db.rename_table('myapp_bar', 'myapp_foo')                                                                                                                        
        db.send_create_signal('myapp', ['Foo'])

Field not NULL but without default

In some cases you alter a model moving a relation like a ForeignKey

class Voice(models.Model):
    class Meta:
        abstract = True

    value = models.CharField("valore", max_length=30, null=True)
    abbreviazione = models.CharField(max_length=30, null=True,unique=True)
    # TODO: controllare che l'abbreviazione sia unica

    def __unicode__(self):
        return '%s' % self.abbreviazione
        
class Gruppo(Voice):
    pass
    
class Qualita(Voice):
    pass
class Qualita(Voice):
    gruppo = models.ForeignKey(Gruppo)

south asks for default value for existing rows

$ python manage.py schemamigration material --auto
 ? The field 'Qualita.gruppo' does not have a default specified, yet is NOT NULL.
 ? Since you are adding this field, you MUST specify a default
 ? value to use for existing rows. Would you like to:
 ?  1. Quit now, and add a default to the field in models.py
 ?  2. Specify a one-off value to use for existing columns now
 ? Please select a choice: 2
 ? Please enter Python code for your one-off default value.
 ? The datetime module is available, so you can do e.g. datetime.date.today()
 >>> 0
 + Added field gruppo on material.Qualita
Created 0001_auto__add_field_qualita_gruppo.py. You can now apply this migration with: ./manage.py migrate material

Change the resulting migration in something like

class Migration(SchemaMigration):

    def forwards(self, orm):
        g = orm.Gruppo.objects.get_or_create(value="fake", abbreviazione="FAKE")
        pk = g.pk

        # Adding field 'Qualita.gruppo'
        db.add_column('material_qualita', 'gruppo',
                      self.gf('django.db.models.fields.related.ForeignKey')(default=pk, to=orm['material.Gruppo']),
                      keep_default=False)


    def backwards(self, orm):
        # Deleting field 'Qualita.gruppo'
        db.delete_column('material_qualita', 'gruppo_id')

Eventi trigger in sospeso

Probably you are trying to modify SCHEMA and a data migration together, switch to two separate migrations.

http://stackoverflow.com/questions/12838111/south-cannot-alter-table-because-it-has-pending-trigger-events

Data migration

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Adding model 'DealImage'
        db.create_table(u'deal_dealimage', (
            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
            ('image', self.gf('ajaximage.fields.AjaxImageField')()),
            ('deal', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['deal.Deal']),)
        ))
        db.send_create_signal(u'deal', ['DealImage'])

        # attach the old image with the new related model
        for deal in orm['deal.deal'].objects.all():
            orm['deal.dealimage'].objects.create(
                deal=deal,
                image=deal.immagine,
            )

        # Deleting field 'Deal.immagine'
        db.delete_column(u'deal_deal', 'immagine')



    def backwards(self, orm):
        # Deleting model 'DealImage'
        db.add_column(u'deal_deal', 'immagine',
                      self.gf('django.db.models.fields.files.ImageField')(max_length=100, default='miao'),
                      keep_default=False)

        for deal in orm['deal.deal'].objects.all():
            # we take the first image
            image = orm['deal.dealimage'].objects.filter(deal=deal).first()
            deal.immagine = image.image
            deal.save()

        db.delete_table(u'deal_dealimage')

    models = {
        u'deal.deal': {
            'Meta': {'ordering': "['inizio_asta']", 'object_name': 'Deal'},
            'attiva': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            'categoria': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['deal.Categoria']"}),
            'codice_asta': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
            'descrizione': ('django.db.models.fields.TextField', [], {}),
            'descrizione_breve': ('django.db.models.fields.CharField', [], {'max_length': '250'}),
            'durata_asta': ('timedelta.fields.TimedeltaField', [], {}),
            'fornitore': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['deal.Provider']"}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'immagine': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
            'inizio_asta': ('django.db.models.fields.DateTimeField', [], {}),
            'numero_rimanente': ('django.db.models.fields.SmallIntegerField', [], {}),
            'numero_totale': ('django.db.models.fields.SmallIntegerField', [], {}),
            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
            'titolo': ('django.db.models.fields.CharField', [], {'max_length': '200'})
        },
        u'deal.dealimage': {
            'Meta': {'object_name': 'DealImage'},
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'image': ('ajaximage.fields.AjaxImageField', [], {}),
            'deal': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['deal.Deal']"})
        },
    complete_apps = ['deal']
    symmetrical = True

Migrate a multi table inheritance

    def forwards(self, orm):
        # Adding model 'DealGroup'
        db.create_table(u'deal_dealgroup', (
            (u'dealbase_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['deal.DealBase'], unique=True, primary_key=True)),
            ('numero_minimo_partecipanti', self.gf('django.db.models.fields.PositiveSmallIntegerField')()),
            ('numero_massimo_partecipanti', self.gf('django.db.models.fields.PositiveSmallIntegerField')()),
            ('prezzo_base_offerta', self.gf('django.db.models.fields.DecimalField')(max_digits=3, decimal_places=2)),
            ('incremento_tassa_servizio', self.gf('django.db.models.fields.DecimalField')(max_digits=3, decimal_places=2)),
            ('decremento_tassa_servizio', self.gf('django.db.models.fields.DecimalField')(max_digits=3, decimal_places=2)),
        ))
        db.send_create_signal(u'deal', ['DealGroup'])

        # rename Deal -> DealBase
        db.rename_table('deal_deal', 'deal_dealbase')

        # recreating model 'Deal'
        db.create_table(u'deal_deal', (
            (u'dealbase_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['deal.DealBase'], unique=True, primary_key=True)),
        ))
        db.send_create_signal(u'deal', ['Deal'])

        # reactivate the existing deals
        #  since it's tricky assigning base to child
        #  we use a comment in ticket #7623 <https://code.djangoproject.com/ticket/7623>
        for dealbase in orm.DealBase.objects.all():
            deal = orm.Deal(dealbase_ptr=dealbase)
            deal.save_base(raw=True)

In order to install django-suit you can install it with pip

$ pip install django-suit

and then add it to the INSTALLED_APPS remembering to place it before the standard admin page

INSTALLED_APPS = (
    'suit',
    'django.contrib.admin',
    ...
)

TEST

First of all, must understand difference between functional, integration and acceptance tests

>>> from django.test.utils import setup_test_environment
>>> setup_test_environment()
>>> from django.test.client import Client
>>> c = Client()
>>> response = c.get("/")
>>> response.template
[<django.template.Template object at 0x2723dd0>,
 <django.template.Template object at 0x2723f30>,
 <django.template.Template object at 0x273ee10>]
>>> response.context
[ list of Context objects ]

If you want to test https scheme you can override the wsgi.url_scheme keyword argument in a test client request (source)

class AuthTest(TestCase):
    fixtures = ['auth_data.json']
    def test_login(self):

        kwargs = {}
        kwargs["wsgi.url_scheme"] = 'https'

        response = self.client.get(reverse('login'), **kwargs)
        self.assertEqual(response.status_code, 200)

Test queryset from response

    def test_users_list(self):
        '''We want a list without superuser listed'''
        self.assertTrue(self.client.login(username=self.user.username, password='password'))
        response = self.client.get(reverse('users:list'))

        self.assertEqual(response.status_code, 200)

        obtained_qs = response.context['object_list']
        desired_qs  = [self.user.username,]

        self.assertQuerysetEqual(obtained_qs, desired_qs, transform=lambda x:x.username, ordered=False)

Test form

form = response.context[0]['form']
self.assertEqual(form.fields['n_max_house'].initial, 10)

or

form = response.context[0]['form']
self.assertEqual(form.initial['n_max_house'], 10)

Test email

from django.core import mail
from django.test import TestCase

class EmailTest(TestCase):
    def test_send_email(self):
        # Send message.
        mail.send_mail('Subject here', 'Here is the message.',
            'from@example.com', ['to@example.com'],
            fail_silently=False)

        # Test that one message has been sent.
        self.assertEqual(len(mail.outbox), 1)

        # Verify that the subject of the first message is correct.
        self.assertEqual(mail.outbox[0].subject, 'Subject here')

This is an issue that someone complained about (for example here): how make a Django app that works fine with namespaces?

What I mean is, if as usual I put this in the urls.py file

urlpatterns = (
    url('^whatever$', include(whatever.urls, namespace='foo')),
)

how is supposed to be coded a reverse() call in a view of the whatever application in order to be reusable, if the namespace is set in the general include?

For a solution I looked at the obvious application that has a namespace but it's not passed in the include, the admin: if you look at the include statement, it looks like

    url(r'^admin/', include(admin.site.urls)),

this is found in django/contrib/admin/sites.py, implemented as

# This global object represents the default admin site, for the common case.
# You can instantiate AdminSite in your own code to create a custom admin site.
site = AdminSite()

the important parts is in the AdminSite class are

    def __init__(self, name='admin', app_name='admin'):
        self._registry = {}  # model_class class -> admin_class instance
        self.name = name
        self.app_name = app_name
        self._actions = {'delete_selected': actions.delete_selected}
        self._global_actions = self._actions.copy()

    def get_urls(self):
        from django.conf.urls import patterns, url, include

        if settings.DEBUG:
            self.check_dependencies()

        def wrap(view, cacheable=False):
            def wrapper(*args, **kwargs):
                return self.admin_view(view, cacheable)(*args, **kwargs)
            return update_wrapper(wrapper, view)

        # Admin-site-wide views.
        urlpatterns = patterns('',
            url(r'^$',
                wrap(self.index),
                name='index'),
            url(r'^logout/$',
                wrap(self.logout),
                name='logout'),
            url(r'^password_change/$',
                wrap(self.password_change, cacheable=True),
                name='password_change'),
            url(r'^password_change/done/$',
                wrap(self.password_change_done, cacheable=True),
                name='password_change_done'),
            url(r'^jsi18n/$',
                wrap(self.i18n_javascript, cacheable=True),
                name='jsi18n'),
            url(r'^r/(?P<content_type_id>\d+)/(?P<object_id>.+)/$',
                wrap(contenttype_views.shortcut),
                name='view_on_site'),
            url(r'^(?P<app_label>\w+)/$',
                wrap(self.app_index),
                name='app_list')
        )

        # Add in each model's views.
        for model, model_admin in six.iteritems(self._registry):
            urlpatterns += patterns('',
                url(r'^%s/%s/' % (model._meta.app_label, model._meta.model_name),
                    include(model_admin.urls))
            )
        return urlpatterns

    @property
    def urls(self):
        return self.get_urls(), self.app_name, self.name

In the documentation there is a section about multiple admin sites in the same urlconf

BTW, this is an approach to write a urls in order to create something usable

# payment/urls.py
from django.conf.urls import patterns, include, url

from .views import paga, paypal_return, paypal_cancel


urlpatterns = patterns('',
    url(r'^payment/(?P<bet_id>\d+)/$', paga, name='pagina_pagamenti'),
    url(r'^payment/return/$', paypal_return, name='return_payment'),
    url(r'^payment/cancel/$', paypal_cancel, name='cancel_payment'),
)

class Urls(object):
    def __init__(self, namespace='payment'):
        self.namespace = namespace

    def get_urls(self):
        return urlpatterns

    @property
    def urls(self):
        return self.get_urls(), self.namespace, 'payment'

# core/urls.py
from payment.urls import Urls as payment_urls

urlpatterns = patterns('',
    url('', include(payment_urls('paymentt').urls)),
)

If you want to reverse you can write in the view

reverse('payment:return_payment', current_app=resolve(self.request.path).namespace))

in the template you can write

def some_view(request):
    return render_to_response('some_template.html', )

Here seems there are some clues about reversing with namespace in tag and programmatically.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment