Skip to content

Instantly share code, notes, and snippets.

@fiee
Created September 30, 2011 08:14
Show Gist options
  • Save fiee/1253058 to your computer and use it in GitHub Desktop.
Save fiee/1253058 to your computer and use it in GitHub Desktop.
Generic base classes for my django models, superseded by fiee/fiee-dorsale
# -*- coding: utf-8 -*-
import datetime
from django.contrib import admin
from django.contrib.sites.models import Site
class BaseAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
if not change:
obj.createdby = request.user
obj.createdon = datetime.datetime.now()
obj.site = Site.objects.get_current()
obj.deleted = False
obj.lastchangedby = request.user
obj.lastchangedon = datetime.datetime.now()
obj.save()
def queryset(self, request):
# TODO: query (group) permissions
qs = self.model._default_manager.get_query_set()
ordering = self.ordering or ()
if not request.user.is_superuser:
qs = qs.filter(created_by=request.user)
if ordering:
qs = qs.order_by(*ordering)
return qs
def has_class_permission(self, request, obj=None):
return super(BaseAdmin, self).has_change_permission(request, obj)
def has_change_permission(self, request, obj=None):
if not self.has_class_permission(request, obj):
return False
if obj is not None and not request.user.is_superuser and request.user.id != obj.author.id:
# TODO: Permissions!
return False
opts = self.opts
return request.user.has_perm(opts.app_label + '.' + opts.get_change_permission())
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import datetime
from django.db import models
from django import forms
from django.forms import ModelForm
from django.contrib.sites.models import Site
class BaseModelForm(ModelForm):
class Meta:
#exclude = ['createdon','createdby','lastchangedon','lastchangedby','site','deleted',]
# we don't need exclude, since editable=False
pass
def save(self, user, commit=True):
obj = super(BaseModelForm, self).save(commit=False)
obj.lastchangedby = user
obj.lastchangedon = datetime.datetime.now()
if not obj.pk:
obj.createdby = user
obj.createdon = datetime.datetime.now()
obj.deleted = False
if commit:
obj.save()
self.save_m2m()
return obj
def ModelFormFactory(some_model):
"Why should we define an own class for every model?"
name = some_model.__name__+'Form'
mf = type(name, (BaseModelForm,), {})
mf.Meta.model = some_model
return mf
{% extends "root.html" %}
{% load i18n %}
{% block title %}{{ object_example.classname_plural }}{% endblock %}
{% block main_content %}
<h2>{% block pagetitle %}{{ object_list.paginator.count }} {{ object_example.classname_plural }}{% endblock %}</h2>
{% include "pagination.html" %}
<table class="itemtable">
<thead>
<tr>
{% for fn in object_example.fieldnames_verbose %}
<th>{{ fn }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for item in object_list.object_list %}
<tr>
{% for fv in item.fieldvalues %}
{% if forloop.counter0 < 2 %}
<td><a href="{{ item.get_absolute_url }}">{{ fv }}</a></td>
{% else %}
<td>{{ fv }}</td>
{% endif %}
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
{% include "pagination.html" %}
{% endblock %}
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import datetime
import re
from django.conf import settings
from django.db import models
from django.forms import fields
from django.utils.translation import ugettext_lazy as _
from django.contrib.contenttypes.models import ContentType
from django.contrib.sites.models import Site
from django.contrib.sites.managers import CurrentSiteManager
from django.contrib.auth.models import User
#from south.modelsinspector import add_introspection_rules
class FilterForeignKey(models.ForeignKey):
"""
Replacement for ForeignKey that implements filtering by the FK, e.g. User
class MyModel(BaseModel):
pass
MyModel.for_creator(request.user) # like MyModel.objects.filter(user=request.user)
see
http://lazypython.blogspot.com/2008/11/more-laziness-with-foreign-keys.html
"""
def __init__(self, to, to_field=None, rel_class=models.ManyToOneRel, **kwargs):
# copied from original ForeignKey class to get the name
try:
to_name = to._meta.object_name.lower()
except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
assert isinstance(to, basestring), "%s(%r) is invalid. First parameter to FilterForeignKey must be either a model, a model name, or the string %r" % (self.__class__.__name__, to, RECURSIVE_RELATIONSHIP_CONSTANT)
else:
assert not to._meta.abstract, "%s cannot define a relation with abstract class %s" % (self.__class__.__name__, to._meta.object_name)
self.manager_name = kwargs.pop('manager_name', 'for_%s' % to_name)
super(FilterForeignKey, self).__init__(to, to_field, rel_class, **kwargs)
def contribute_to_class(self, cls, name):
super(FilterForeignKey, self).contribute_to_class(cls, name)
class MyManager(models.Manager):
def __call__(self2, obj):
return cls._default_manager.filter(**{self.name: obj})
cls.add_to_class(self.manager_name, MyManager())
#add_introspection_rules([], ["^%s\.models\.FilterForeignKey" % settings.PROJECT_NAME])
class BaseModel(models.Model):
"""
Abstract base class of all fiee models.
Provides ...
1) automatic administrations fields:
createdby, lastchangedby
createdon, lastchangedon
:deleted: you can't delete our objects any more, they just get marked as deleted
2) additional class methods from FilterForeignKey and django’s Site:
:for_creator(user): queryset of objects created by that user
:for_lastchanger(user): queryset of objects last-changed by that user
:for_site(site): queryset of objects that belong to that tenant's site
:on_site: queryset of objects for the current site
3) additional meta info methods for generic view:
:fieldnames_verbose: list of translated field names
:fieldnames: list of raw field names
:fieldvalues: list of field values
:classname: translated class name
:classname_plural: translated plural class name
:get_absolute_url: /module/model/id/ (doesn't work?)
"""
createdby = FilterForeignKey(User, verbose_name=_('created by'), related_name="%(app_label)s_%(class)s_createdset", manager_name='creator', editable=False, default=settings.ANONYMOUS_USER_ID, help_text=_(u'the user that was logged in when this item was created'))
createdon = models.DateTimeField(verbose_name=_(u'created on'), null=True, editable=False, help_text=_(u'date and time when this item was created'))
lastchangedby = FilterForeignKey(User, verbose_name=_('last changed by'), related_name="%(app_label)s_%(class)s_changedset", manager_name='lastchanger', editable=False, default=settings.ANONYMOUS_USER_ID, help_text=_(u'the user that was logged in when this item was changed the last time'))
deleted = models.BooleanField(verbose_name=_('deleted?'), editable=False, default=False, help_text=_(u'Is this item marked as deleted?'))
lastchangedon = models.DateTimeField(verbose_name=_(u'last changed on'), null=True, editable=False, help_text=_(u'date and time when this item was changed the last time'))
site = FilterForeignKey(Site, verbose_name=_(u'tenant’s site'), editable=False, default=1, help_text=_(u'site of the related customer/project/team')) # related_name="%(app_label)s_%(class)s_siteset",
objects = models.Manager() # must stand before CurrentSiteManager, if admin should see all objects
on_site = CurrentSiteManager('site')
class Meta:
abstract = True
permissions = [
('view_item', _(u'View Item')),
]
def delete(self, *args, **kwargs):
"we never really delete anything"
self.deleted = True
self.save(*args, **kwargs)
def fields(self):
return (f for f in self._meta.fields)
def fieldnames_verbose(self):
return (f.verbose_name for f in self._meta.fields if f.editable)
def fieldnames(self):
return [f.name for f in self._meta.fields if f.editable]
def fieldvalues(self):
return (getattr(self, n) for n in self.fieldnames())
def classname(self):
return self._meta.verbose_name
def classname_plural(self):
return self._meta.verbose_name_plural
@models.permalink
def get_absolute_url(self):
mo = ContentType.model_class(self)
return ('%s.%s' % (mo.app_label, mo.model), self.id)
{% load i18n %}
<!-- using some icons from jQuery-UI -->
{% if object_list.has_other_pages %}
<div class="pagination">
<span class="arrows">
{% if object_list.has_previous %}
<a href="?page=1" class="button" title="{% trans "First" %}"><span class="ui-icon ui-icon-arrowthickstop-1-w"></span></a>
<a href="?page={{ object_list.previous_page_number }}" class="button" title="{% trans "Previous" %}"><span class="ui-icon ui-icon-arrowthick-1-w"></span></a>
{% endif %}
</span>
<span class="current"> {{ object_list.number }}/{{ object_list.paginator.num_pages }} </span>
<span class="arrows">
{% if object_list.has_next %}
<a href="?page={{ object_list.next_page_number }}" class="button" title="{% trans "Next" %}"><span class="ui-icon ui-icon-arrowthick-1-e"></span></a>
<a href="?page={{ object_list.paginator.num_pages }}" class="button" title="{% trans "Last" %}"><span class="ui-icon ui-icon-arrowthickstop-1-e"></span></a>
{% endif %}
</span>
</div>
{% endif %}
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from django.conf import settings
from django.shortcuts import render_to_response
from django.http import HttpResponse, HttpResponseRedirect
from django.template import RequestContext
from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator, InvalidPage, EmptyPage
from django.utils.translation import ugettext_lazy as _
from myapp.forms import *
import myapp1, myapp2
def get_model(app_name, model_name):
"""Find a Model"""
app_name = app_name.lower()
model_name = model_name.title()
try:
# TODO: How can I get the module generically from a string?
if app_name == 'myapp1':
#object_model = myapp1.models.__dict__[model_name]
if app_name == 'myapp2':
#object_model = myapp2.models.__dict__[model_name]
return object_model
except KeyError:
return None
def render_404(request, params):
"""return a 404 error with my template"""
params['path'] = request.get_full_path()
response = render_to_response('404.html', params, context_instance=RequestContext(request))
response.status_code=404
return response
@login_required
def home(request):
return render_to_response('root.html', {}, context_instance=RequestContext(request))
@login_required
def list_items(request, app_name, model_name):
object_model = get_model(app_name, model_name)
if object_model:
object_example = object_model()
paginator = Paginator(object_model.on_site.all(), int(getattr(object_model, 'items_per_page', getattr(settings, 'ITEMS_PER_PAGE', 10))), orphans=2)
# check for a valid number
try:
page = int(request.GET.get('page', '1'))
except ValueError:
page = 1
# check if page is valid
try:
object_list = paginator.page(page)
except (EmptyPage, InvalidPage):
object_list = paginator.page(paginator.num_pages)
return render_to_response('list_items.html', locals(), context_instance=RequestContext(request))
else:
return render_404(request, locals())
@login_required
def show_item(request, app_name, model_name, id=None):
object_model = get_model(app_name, model_name)
form_model = ModelFormFactory(object_model)
item = object_model.objects.get(pk=id)
form = form_model(instance=item)
return render_to_response('show_item.html', locals(), context_instance=RequestContext(request))
@fiee
Copy link
Author

fiee commented Sep 30, 2011

Somewhat hackish, looking for advice about more elegant code regarding class introspection etc.

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