tutorial - django admin: vista separada de solo lectura y vista de cambio
views django (5)
Aquí hay una respuesta que literalmente hace lo que le pedí con solo unas pocas líneas de código y solo un par de cambios de plantilla:
class MyModelAdmin(admin.ModelAdmin):
fieldsets = [...]
def get_readonly_fields(self, request, obj=None):
if ''edit'' not in request.GET:
return <list all fields here>
else:
return self.readonly_fields
Ahora, la URL habitual de change_form producirá un solo cambio change_form, pero si agrega "? Edit = 1" a la URL, podrá editar.
La plantilla change_form también se puede personalizar dependiendo de si "? Edit = 1" está en la URL. Para hacer esto, ponga ''django.core.context_processors.request''
en TEMPLATE_CONTEXT_PROCESSORS
en settings.py
, y luego use request.GET.edit
en la plantilla.
Por ejemplo, para agregar un botón "Editar" cuando no esté en modo de edición, inserte
{% if not request.GET.edit %}
<li><a href="?edit=1">Edit</a></li>
{% endif %}
justo después de <ul class="object-tools">
en change_form.html
.
Como otro ejemplo, cambiar change_form.html
para contener
{% if save_on_top and request.GET.edit %}{% submit_row %}{% endif %}
significará que la fila de envío solo se mostrará en el modo de edición. También se pueden ocultar los botones Eliminar en líneas, etc., utilizando este método.
Para referencia, aquí está lo que puse en settings.py
:
TEMPLATE_CONTEXT_PROCESSORS = (
''django.contrib.auth.context_processors.auth'',
''django.core.context_processors.debug'',
''django.core.context_processors.i18n'',
''django.core.context_processors.media'',
''django.contrib.messages.context_processors.messages'',
# Above here are the defaults.
''django.core.context_processors.request'',
)
Me gustaría usar el administrador de django para producir una vista de solo lectura de un objeto que contenga un botón "Editar" que le cambie a la vista de cambio habitual del mismo objeto.
Sé cómo usar los atributos de solo lectura para producir una vista de solo lectura, pero no sé cómo producir dos vistas, una de solo lectura y otra que permita cambios.
Me gustaría reutilizar la mayor parte posible de la interfaz de administración, en lugar de escribir una vista desde cero.
Tenga en cuenta que esta pregunta no es sobre permisos: todos los usuarios tendrán permiso para cambiar los objetos. Es solo que preferiría que no usen change_view a menos que tengan la intención de hacer cambios, reduciendo el riesgo de cambios accidentales o simultáneos.
Deberá cambiar la plantilla que usa el administrador de django para el formulario modelo. Haga que sea de solo lectura y agregue un botón a la plantilla original vinculada a otra URL.
Nota:
Desaliento mucho este enfoque, definitivamente no evitará cambios simultáneos. Esto debe resolverse con bloqueo.
Además, recomiendo usar django-reversion para mantener el historial de objetos y eliminar el riesgo de "cambios accidentales".
El siguiente código es la implementación de un administrador de solo lectura con proxy models.
Modelos.py
// modelo real
class CompetitionEntry(models.Model):
pass
// modelo de proxy
class ReviewEntry(CompetitionEntry):
class Meta:
proxy = True
def save(self, *args, **kwargs):
pass
admin.py
// administrador editable
class CompetitionEntryAdmin(admin.ModelAdmin):
pass
admin.site.register(CompetitionEntry, CompetitionEntryAdmin)
// administrador de solo lectura (asigne solo permiso de "cambio" para esto)
class ReviewEntryAdmin(admin.ModelAdmin):
pass
admin.site.register(ReviewEntry, ReviewEntryAdmin)
Puede crear una vista personalizada y mostrar su objeto allí.
Para crear una vista personalizada en un módulo de administración, anule el método get_urls()
:
class MyAdmin(admin.ModelAdmin):
…
def get_urls(self):
urls = super(MyAdmin, self).get_urls()
my_urls = patterns('''',
url(r''^custom_view/(?P<my_id>/d+)/$'', self.admin_site.admin_view(self.custom_viem), name=''custom_view'')
)
return my_urls + urls
def custom_view(self, request, my_id):
"""Define your view function as usual in views.py
Link to this view using reverse(''admin:custom_view'')
"""
from myapp import views
return views.custom_view(request, my_id, self)
En views.py:
def custom_view(request, object_id, model_admin):
admin_site = model_admin.admin_site
opts = model_admin.model._meta
my_object = get_object_or_404(MyObject, pk=object_id)
# do stuff
context = {
''admin_site'': admin_site.name,
''opts'': opts,
''title'': _(''My custom view''),
''root_path'': ''%s'' % admin_site.root_path,
''app_label'': opts.app_label,
''my_object'': my_object,
}
return render_to_response(''my_template.html'', context,
context_instance=RequestContext(request))
En su plantilla, use {% extends "admin/base_site.html" %}
para mantener la apariencia y la apariencia del administrador.
Yo sugeriría reconsiderar el uso de vistas personalizadas. Con la ayuda de DetailView
genérico, deberás escribir literalmente dos líneas de código.La plantilla tampoco requerirá mucho trabajo. Simplemente extiende la plantilla estándar change_form.html , anulando el bloque field_sets
.
Sé cómo usar los atributos de solo lectura para producir una vista de solo lectura, pero no sé cómo producir dos vistas, una de solo lectura y otra que permita cambios.
En realidad, puede registrar un modelo en el administrador dos veces [1], utilizando modelos de proxy . (Existen algunas inconsistencias con los permisos para los modelos de proxy, pero puede que no sea un problema en su caso).
Parece que también es posible registrar varios sitios de administración [2].
Me gustaría reutilizar la mayor parte posible de la interfaz de administración, en lugar de escribir una vista desde cero.
La reutilización de la interfaz como tal tiene poco que ver con las vistas, ya que se trata principalmente de cosas relacionadas con la plantilla y el estilo. La vista, sin embargo, debe proporcionar el contexto de plantilla necesario para la reutilización de la interfaz, tal como lo señaló correctamente.
Si decide ModelAdmin
varias vistas por un ModelAdmin
, entonces podría ser útil que compruebe cómo el proyecto django-reversion
implementa su integración de administrador: reversion/admin.py .