Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 17cd91c
Showing
9 changed files
with
307 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
*.py[co] |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
{% if form %}To edit this page, click <a href="#my_editable_content" class="inline_edit_button">here</a>{% endif %} | ||
|
||
<div id="my_editable_content"> | ||
<div class="inline_editable_content"> | ||
<h2>{{ object.title }}</h2> | ||
{{ object.body }} | ||
</div> | ||
<form action="" method="post" class="inline_editable_form"> | ||
{{ form }} | ||
<input type="submit" value="Save" /> | ||
</form> | ||
</div> | ||
|
||
class Author(models.Model): | ||
name = models.CharField(max_length=100) | ||
|
||
class Book(models.Model): | ||
author = models.ForeignKey(Author) | ||
title = models.CharField(max_length=100) | ||
|
||
|
||
InlineUpdateView().as_view(model=Publisher) | ||
|
||
uses "books/publisher_inline_form.html" | ||
|
||
|
||
|
||
from django.forms.models import inlineformset_factory | ||
from embedded_inline_form import ModelForm | ||
|
||
class AuthorBooksForm(ModelForm): | ||
class Meta: | ||
model = Author | ||
class Forms: | ||
inlines = { | ||
'books': inlineformset_factory(Author, Book), | ||
} | ||
|
||
class AuthorUpdateView(UpdateView): | ||
form_class = AuthorBooksForm | ||
model = Author | ||
|
||
AuthorUpdateView().as_view() |
Empty file.
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
# Copyright (c) 2010, Stanislas Guerra. | ||
# All rights reserved. | ||
# This document is licensed as free software under the terms of the | ||
# BSD License: http://www.opensource.org/licenses/bsd-license.php | ||
|
||
|
||
from django.forms.models import ModelFormMetaclass | ||
|
||
|
||
class ModelFormOptions(object): | ||
def __init__(self, options=None): | ||
self.inlines = getattr(options, 'inlines', {}) | ||
|
||
class ModelFormMetaclass(ModelFormMetaclass): | ||
def __new__(cls, name, bases, attrs): | ||
new_class = super(ModelFormMetaclass, cls).__new__(cls, name, bases, attrs) | ||
new_class._forms = ModelFormOptions(getattr(new_class, 'Forms', None)) | ||
return new_class | ||
|
||
class ModelForm(forms.ModelForm): | ||
""" | ||
Add to ModelForm the ability to declare inline formsets. | ||
It save you from the boiler-plate implementation of cross validation/saving of such | ||
forms in the views. | ||
You should use It in the admin's forms if you need the inherit them in your apps | ||
because there is not multi-inherance. | ||
>>> class Program(models.Model): | ||
... name = models.CharField(max_length=100, blank=True) | ||
>>> class ImageProgram(models.Model): | ||
... image = models.ImageField('image') | ||
... program = models.ForeignKey(Programm) | ||
>>> class Ringtone(models.Model): | ||
... sound = models.FileField('sound') | ||
... program = models.ForeignKey(Programm) | ||
Use It in your admin.py instead of django.forms.ModelForm: | ||
>>> class ProgramAdminForm(ModelForm): | ||
... class Meta: | ||
... model = Program | ||
... def clean(self): | ||
... cleaned_data = self.cleaned_data | ||
... # stuff | ||
... return cleaned_data | ||
In your app, say you declare the following inline formsets: | ||
>>> ImageProgramFormSet = inlineformset_factory(Program, ImageProgram, | ||
... form=ImageProgramForm, max_num=6) | ||
>>> RingToneFormSet = inlineformset_factory(Program, RingTone, form=RingtoneProgramForm) | ||
You can bind them in your program's form: | ||
>>> class MyProgramForm(ProgramAdminForm): | ||
... class Forms: | ||
... inlines = { | ||
... 'images': ImageProgramFormSet, | ||
... 'ringtones': RingToneFormSet, | ||
... } | ||
And instanciate It: | ||
>>> program_form = MyProgramForm(request.POST, request.FILES, prefix='prog') | ||
In the template, you access the inlines like that : | ||
{{ program_form.inlineformsets.images.management_form }} | ||
{{ program_form.inlineformsets.images.non_form_errors }} | ||
<table> | ||
{{ program_form.inlineformsets.images.as_table }} | ||
</table> | ||
""" | ||
|
||
__metaclass__ = ModelFormMetaclass | ||
|
||
def __init__(self, *args, **kwargs): | ||
super(ModelForm, self).__init__(*args, **kwargs) | ||
if hasattr(self._forms, 'inlines'): | ||
self.inlineformsets = {} | ||
for key, FormSet in self._forms.inlines.items(): | ||
self.inlineformsets[key] = FormSet(self.data or None, self.files or None, | ||
prefix=self._get_formset_prefix(key), | ||
instance=self.instance) | ||
|
||
def save(self, *args, **kwargs): | ||
instance = super(ModelForm, self).save(*args, **kwargs) | ||
if hasattr(self._forms, 'inlines'): | ||
for key, FormSet in self._forms.inlines.items(): | ||
fset = FormSet(self.data, self.files, prefix=self._get_formset_prefix(key), | ||
instance=instance) | ||
fset.save() | ||
return instance | ||
|
||
def has_changed(self, *args, **kwargs): | ||
has_changed = super(ModelForm, self).has_changed(*args, **kwargs) | ||
if has_changed: | ||
return True | ||
else: | ||
for fset in self.inlineformsets.values(): | ||
for i in range(0, fset.total_form_count()): | ||
form = fset.forms[i] | ||
if form.has_changed(): | ||
return True | ||
return False | ||
|
||
def _get_formset_prefix(self, key): | ||
return u'%s_%s' % (self.prefix, key.upper()) | ||
|
||
def _clean_form(self): | ||
super(ModelForm, self)._clean_form() | ||
for key, fset in self.inlineformsets.items(): | ||
for i in range(0, fset.total_form_count()): | ||
f = fset.forms[i] | ||
if f.errors: | ||
self._errors['_%s_%d' %(fset.prefix, i)] = f.non_field_errors |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from django.db import models | ||
|
||
# Create your models here. |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
|
||
jquery_wrapper($){ | ||
$(function(){ | ||
$("inline_edit_button").click(function(){ | ||
editable = $(this).attr("href"); | ||
$(editable).find("inline_editable_content").hide(); | ||
$(editable).find("inline_editable_form").show(); | ||
} | ||
}); | ||
)(jquery); |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
from django.views import View, DetailView, UpdateView | ||
from django.utils.decorators import classonlymethod | ||
from django.models import Permission | ||
from django.core.exceptions import ImproperlyConfigured | ||
|
||
|
||
def conditional_dispatch(condition_func, true_view, false_view): | ||
def dispatch(request, *args, **kwargs): | ||
if condition_func(request, *args, **kwargs): | ||
return true_view(request, *args, **kwargs) | ||
else: | ||
return false_view(request, *args, **kwargs) | ||
return dispatch | ||
|
||
def dict_diff(dict1, dict2): | ||
diff_set = set(dict1.iteritems()) - set(dict2.iteritems()) | ||
return dict(diff_set) | ||
|
||
class ConditionalDispatchView(object): | ||
""" | ||
TODO: allow overriding this class, to set items on both subclasses | ||
""" | ||
class Meta: | ||
pass | ||
@classonlymethod | ||
def as_view(cls, condition_func=None, | ||
true_view_class=None, false_view_class=None, **initkwargs | ||
): | ||
if true_view_class is None | ||
try: | ||
true_view_class = cls.Meta.true_view_class | ||
except AttributeError: | ||
raise ImproperlyConfigured( | ||
"No view to direct to when condition_function returns" | ||
" True. Either provide a true_view_class, or define a" | ||
" true_view_class property on this view's Meta class." | ||
) | ||
if false_view_class is None | ||
try: | ||
false_view_class = cls.Meta.false_view_class | ||
except AttributeError: | ||
raise ImproperlyConfigured( | ||
"No view to direct to when condition_function returns" | ||
" False. Either provide a false_view_class, or define a" | ||
" false_view_class property on this view's Meta class." | ||
) | ||
|
||
# get properties defined on this subclass as base initkwargs | ||
base_initkwargs = dict_diff(cls, ConditionalDispatchView) | ||
base_initkwargs.update(initkwargs) | ||
|
||
# remove the Meta class; that's for us, not the view class | ||
base_initkwargs.pop('Meta', None) | ||
|
||
# create class-specific keyword arguments | ||
true_initkwargs = {} | ||
false_initkwargs = {} | ||
|
||
for k, v in base_initkwargs.iter_items(): | ||
if hasattr(true_view_class, k): | ||
true_initkwargs[key] = v | ||
if hasattr(false_view_class, k): | ||
false_initkwargs[key] = v | ||
|
||
true_view = true_view_class().as_view(**true_initkwargs) | ||
false_view = false_view_class().as_view(**false_initkwargs) | ||
|
||
if condition_func is None: | ||
# default condition_func checks if user 'can_change' the object | ||
model = true_view_class(**true_initkwargs).get_queryset().model | ||
app_label = model._meta.app_label | ||
change_perm = model._meta.get_change_permission() | ||
def condition_func(request, *args, **kwargs) | ||
return request.user.has_perm(app_label + '.' + change_perm) | ||
|
||
return conditional_dispatch(condition_func, true_view, false_view) | ||
|
||
class InlineUpdateView(ConditionalDispatchView): | ||
template_name_suffix = '_inline_form' | ||
class Meta: | ||
true_view_class = UpdateView | ||
false_view_class = DetailView | ||
|
||
|
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
|
||
# from http://www.travisswicegood.com/2010/01/17/django-virtualenv-pip-and-fabric/ | ||
|
||
from django.conf import settings | ||
from django.core.management import call_command | ||
|
||
def main(): | ||
# Dynamically configure the Django settings with the minimum necessary to | ||
# get Django running tests | ||
settings.configure( | ||
INSTALLED_APPS=( | ||
'django.contrib.auth', | ||
'django.contrib.contenttypes', | ||
'django.contrib.admin', | ||
'django.contrib.sessions', | ||
'multiforloop', | ||
), | ||
# Django replaces this, but it still wants it. *shrugs* | ||
DATABASES = { | ||
'default': { | ||
'ENGINE': 'django.db.backends.sqlite3', | ||
'NAME': '/tmp/django_login.db', | ||
} | ||
}, | ||
MEDIA_ROOT = '/tmp/django_login_test_media/', | ||
ROOT_URLCONF = '', | ||
DEBUG = True, | ||
TEMPLATE_DEBUG = True, | ||
) | ||
|
||
#call_command('syncdb') | ||
|
||
# Fire off the tests | ||
call_command('test', 'multiforloop') | ||
|
||
|
||
if __name__ == '__main__': | ||
main() | ||
|
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from distutils.core import setup | ||
|
||
setup( | ||
name='django-inline-edit', | ||
version='0.1dev', | ||
packages=['inline_edit',], | ||
author='Gabriel Grant', | ||
author_email='g@briel.ca', | ||
license='LGPL', | ||
long_description=open('README').read(), | ||
url='http://github.org/gabrielgrant/django-inline-edit/', | ||
) | ||
|