Created
September 30, 2011 08:14
-
-
Save fiee/1253058 to your computer and use it in GitHub Desktop.
Generic base classes for my django models, superseded by fiee/fiee-dorsale
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- 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()) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% 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 %} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% 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 %} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Somewhat hackish, looking for advice about more elegant code regarding class introspection etc.