Merge remote-tracking branch 'origin/develop' into inline-formsets

This commit is contained in:
Andrew Ingram 2013-05-21 00:11:28 +01:00
commit c051b8807a
18 changed files with 170 additions and 57 deletions

View file

@ -15,3 +15,4 @@ Developers
* Andrew Ingram (@AndrewIngram)
* Gregor Müllegger (@gregmuellegger)
* Rivo Laks (@rivol)
* Chris Lawlor (@chrislawlor)

View file

@ -10,6 +10,11 @@ django-admin2
One of the most useful parts of ``django.contrib.admin`` is the ability to configure various views that touch and alter data. django-admin2 is a complete rewrite of that library using modern Class-Based Views and enjoying a design focused on extendibility and adaptability. By starting over, we can avoid the legacy code and make it easier to write extensions and themes.
Contributing
=============
Yes please! Please read our formal contributing document at: https://django-admin2.readthedocs.org/en/latest/contributing.html
Features (current)
====================
@ -39,11 +44,6 @@ Requirements
* django-floppyforms
* Sphinx (for documentation)
Contributing
=============
Yes please! Please read our formal contributing document at: https://github.com/pydanny/django-admin2/blob/master/docs/contributing.rst
Basic Pattern
==============

View file

@ -1,3 +1,7 @@
"""
WARNING: This file about to undergo major refactoring by @pydanny per Issue #99.
"""
from django.conf.urls import patterns, include, url
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
@ -79,7 +83,6 @@ class Admin2(object):
Autodiscovers all admin2.py modules for apps in INSTALLED_APPS by
trying to import them.
"""
apps = []
for app_name in [x for x in settings.INSTALLED_APPS]:
try:
import_module("%s.admin2" % app_name)

View file

@ -1,4 +1,5 @@
"""
WARNING: This file about to undergo major refactoring by @pydanny per Issue #99.
For wont of a better name, this module is called 'models'. It's role is
synonymous with the django.contrib.admin.sites model.
@ -13,10 +14,11 @@ from django.db.models import get_models, signals
from djadmin2 import apiviews
from djadmin2 import views
try:
import floppyforms as forms
except ImportError:
from django import forms
MODEL_ADMIN_ATTRS = (
'list_display', 'list_display_links', 'list_filter',
'admin', 'has_permission', 'has_add_permission',
'has_edit_permission', 'has_delete_permission',
)
class BaseAdmin2(object):
@ -92,6 +94,7 @@ class ModelAdmin2(BaseAdmin2):
Warning: This class is targeted for reduction.
It's bloated and ugly.
"""
list_display = ('__str__',)
list_display_links = ()
list_filter = ()
@ -104,6 +107,7 @@ class ModelAdmin2(BaseAdmin2):
save_on_top = False
verbose_name = None
verbose_name_plural = None
model_admin_attributes = MODEL_ADMIN_ATTRS
create_form_class = None
update_form_class = None
@ -140,7 +144,7 @@ class ModelAdmin2(BaseAdmin2):
'app_label': self.app_label,
'model': self.model,
'model_name': self.model_name,
'model_admin': self,
'model_admin': ImmutableAdmin(self),
}
def get_default_api_view_kwargs(self):
@ -243,6 +247,26 @@ class ModelAdmin2(BaseAdmin2):
def api_urls(self):
return self.get_api_urls(), None, None
class ImmutableAdmin(object):
"""
Only __init__ allows setting of attributes
"""
def __init__(self, model_admin):
""" The __init__ is the only method where the ImmutableModelAdmin allows
for setting of values.
"""
for attr_name in model_admin.model_admin_attributes:
setattr(self, attr_name, getattr(model_admin, attr_name))
self.__delattr__ = self._immutable
self.__setattr__ = self._immutable
def _immutable(self, name, value):
raise TypeError("Can't modify immutable model admin")
def create_extra_permissions(app, created_models, verbosity, **kwargs):
"""
Creates 'view' permissions for all models.
@ -288,4 +312,4 @@ def create_extra_permissions(app, created_models, verbosity, **kwargs):
signals.post_syncdb.connect(create_extra_permissions,
dispatch_uid = "django-admin2.djadmin2.models.create_extra_permissions")
dispatch_uid="django-admin2.djadmin2.models.create_extra_permissions")

View file

@ -1,9 +1,9 @@
<!DOCTYPE html>
{% load i18n %}<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>django-admin2</title>
<title>{% block title %}Site administration{% endblock %} | django-admin2</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Bootstrap -->
<link href="{{ STATIC_URL }}themes/bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen">
@ -16,14 +16,41 @@
<a class="brand" href="{% url 'admin2:dashboard' %}">Django-Admin2</a>
<ul class="nav pull-right">
<li><a href="{% url 'admin2:api-index' %}">API</a></li>
<li><a href="TODO">Log out</a></li>
<li><a tabindex="-1" href="{% url 'admin2:api-index' %}">{% trans "API" %}</a></li>
{% if docsroot %}
<li><a href="{{ docsroot }}">{% trans 'Documentation' %}</a></li>
{% endif %}
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
{% if user.get_full_name %}
{{ user.get_full_name }}
{% else %}
{% blocktrans with user=user.username %}
Logged in as {{ user }}
{% endblocktrans %}
{% endif %}
<b class="caret"></b>
</a>
<ul class="dropdown-menu">
</strong>
{% if user.has_usable_password %}
<li><a tabindex="-1" href="TODO">{% trans "Change password" %}</a></li>
{% endif %}
<li><a tabindex="-1" href="TODO">{% trans "Log out" %}</a></li>
</ul>
</li>
</ul>
</div>
</div>
</div>
<div class="container-fluid">
<div class="row">
<div class="span10">
<h3>{% block page_title %}Site administration{% endblock %}</h3>
</div>
</div>
{% block content %}{% endblock %}
</div>

View file

@ -2,7 +2,6 @@
{% load admin2_urls %}
{% block content %}
<h3>Site administration</h3>
<div class="row">
<div class="span7">

View file

@ -1,5 +1,9 @@
{% extends "admin2/bootstrap/base.html" %}
{% block title %}Add {{ model }}{% endblock %}
{% block page_title %}Add {{ model }}{% endblock %}
{% block content %}
<form method="post">

View file

@ -1,10 +1,16 @@
{% extends "admin2/bootstrap/base.html" %}
{% block title %}Are you sure?{% endblock %}
{% block page_title %}Are you sure?{% endblock %}
{% block content %}
<p>Are you sure you want to delete the {{ model }} "{{ object }}"? All of the following related items will be deleted:</p>
TODO
<form method="post">
{% csrf_token %}
delete {{ object }}
{{ form.as_p }}
<input type="submit"/>
</form>

View file

@ -1,5 +1,9 @@
{% extends "admin2/bootstrap/base.html" %}
{% block title %}{{ object }}{% endblock %}
{% block page_title %}{{ object }}{% endblock %}
{% block content %}
{{ object }}

View file

@ -1,14 +1,11 @@
{% extends "admin2/bootstrap/base.html" %}
{% block title %}Change {{ model }}{% endblock %}
{% block page_title %}Change {{ model }}{% endblock %}
{% block content %}
<div class="row">
<div class="span10">
<h3>Change {{ model }}</h3>
</div>
</div>
<div class="row">
<div class="span12">
<form method="post">

View file

@ -1,13 +1,11 @@
{% extends "admin2/bootstrap/base.html" %}
{% load admin2_urls %}
{% block content %}
<div class="row">
<div class="span10">
<h3>Select {{ model }} to change</h3>
</div>
</div>
{% block title %}Select {{ model }} to change{% endblock %}
{% block page_title %}Select {{ model }} to change{% endblock %}
{% block content %}
<div class="row">
<div class="span12">
@ -36,10 +34,12 @@
</thead>
<tbody>
{% for obj in object_list %}
<td><input type="checkbox"></td>
<td>
<a href="{% url view|admin2_urlname:'update' pk=obj.pk %}">{{ obj }}</a>
</td>
<tr>
<td><input type="checkbox"></td>
<td>
<a href="{% url view|admin2_urlname:'update' pk=obj.pk %}">{{ obj }}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>

View file

@ -9,4 +9,3 @@ def admin2_urlname(view, action):
Converts the view and the specified action into a valid namespaced URLConf name.
"""
return 'admin2:%s_%s_%s' % (view.app_label, view.model_name, action)

View file

@ -60,6 +60,8 @@ class AdminModel2Mixin(Admin2Mixin, AccessMixin):
'has_add_permission': self.model_admin.has_add_permission(self.request),
'has_edit_permission': self.model_admin.has_edit_permission(self.request),
'has_delete_permission': self.model_admin.has_delete_permission(self.request),
'model': self.get_model()._meta.verbose_name,
'model_pluralized': self.get_model()._meta.verbose_name_plural
})
return context
@ -90,7 +92,6 @@ class IndexView(Admin2Mixin, generic.TemplateView):
class ModelListView(Admin2Mixin, generic.ListView):
default_template_name = "model_list.html"
permission_type = 'view'
@ -116,17 +117,17 @@ class ModelEditFormView(AdminModel2Mixin, extra_views.UpdateWithInlinesView):
default_template_name = "model_edit_form.html"
permission_type = 'change'
def get_context_data(self, **kwargs):
context = super(ModelEditFormView, self).get_context_data(**kwargs)
context['model'] = self.get_model()._meta.verbose_name
return context
class ModelAddFormView(AdminModel2Mixin, extra_views.CreateWithInlinesView):
form_class = None
default_template_name = "model_add_form.html"
permission_type = 'add'
def get_context_data(self, **kwargs):
context = super(ModelAddFormView, self).get_context_data(**kwargs)
context['model'] = self.get_model()._meta.verbose_name
return context
def get_success_url(self):
view_name = 'admin2:{}_{}_detail'.format(self.app_label, self.model_name)
return reverse(view_name, kwargs={'pk': self.object.pk})

22
docs/architecture.rst Normal file
View file

@ -0,0 +1,22 @@
================
Architecture
================
.. warning:: This is out of date and will be updated shortly.
Workflow Pieces
----------------
* Apps
* Apps.models
* AdminObj
* Appstore
Workflow
----------------
1. Instantiate Appstore
2. Loop through the Apps then models per App
3. Admin2s are created from models: djadmin2.models.register(Poll)
4. Admin2s contain methods/properties necessaey for UI
5. Views

View file

@ -48,9 +48,9 @@ copyright = u'2013, Daniel Greenfeld'
# built documents.
#
# The short X.Y version.
version = '0.1'
version = '0.2'
# The full version, including alpha/beta/rc tags.
release = '0.1.0'
release = '0.2.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

View file

@ -2,6 +2,10 @@
Contributing
============
.. note:: Before you begin working on your contribution, please read and become familiar with the design_ of ``django-admin2``. The design_ document should hopefully make it clear what our constraints and goals are for the project.
.. _design: https://django-admin2.readthedocs.org/en/latest/design.html
Setup
=====

View file

@ -2,22 +2,42 @@
Design
======
Workflow Pieces
Constraints
------------
This section outlines the design constraints that django-admin2 follows:
1. There will be nothing imported from ``django.contrib.admin``.
2. The original bootstrap/ theme shall contain no UI enhancements beyond the original ``django.contrib.admin`` UI. (However, future themes can and should be experimental.)
3. External package dependencies are allowed but should be very limited.
4. Building a django-admin2 theme cannot involve learning Python, which explains why we are not using tools like django-crispy-forms. (One of our goals is to make it easier for designers to explore theming django-admin2).
Backend Goals
---------------
Rather than creating yet another project that skins ``django.contrib.admin``, our goal is to rewrite ``django.contrib.admin`` from the ground up using Class-Based Views, better state management, and attention to all the lessons learned from difficult admin customizations over the years.
While the internal API for the backend may be drastically different, the end goal is to achieve relative parity with existing functionality in an extendable way:
* Relative functional parity with ``django.contrib.admin``. This is our desire to replicate much of the existing functionality, but not have to worry too much about coding ourselves into an overly-architected corner.
* Extensible presentation and data views in such a way that it does not violate Constraint #4. To cover many cases, we will provide instructions on how to use the REST API to fetch data rather than create overly complex backend code.
Clean code with substantial documentation is also a goal:
1. Create a clearly understandable/testable code base.
2. All classes/methods/functions documented.
3. Provide a wealth of in-line code documentation.
REST API Goals
----------------
* Apps
* Apps.models
* AdminObj
* Appstore
There are a lot of various cases that are hard to handle with pure HTML projects, but are trivial to resolve if a REST API is available. For example, using unmodified ``django.contrib.admin`` on projects with millions of database records combined with foreign key lookups. In order to handle these cases, rather than explore each edge case, ``django-admin2`` provides a RESTFUL API as of version 0.2.0.
Workflow
----------------
Goals:
1. Instantiate Appstore
2. Loop through the Apps then models per App
3. Admin2s are created from models: djadmin2.models.register(Poll)
4. Admin2s contain methods/properties necessaey for UI
5. Views
1. Provide a extendable self-documenting API (django-rest-framework).
2. Reuse components from the HTML view.
3. Backwards compatibility: Use a easily understood API versioning system so we can expand functionality of the API without breaking existing themes.
UI Goals
---------
@ -25,3 +45,4 @@ UI Goals
1. Replicate the old admin UI as closely as possible in the bootstrap/ theme. This helps us ensure that admin2/ functionality has parity with admin/.
2. Once (1) is complete and we have a stable underlying API, experiment with more interesting UI variations.

View file

@ -53,8 +53,9 @@ Content
:maxdepth: 2
contributing
api
design
architecture
api
themes
meta