2011-04-11 12 views
13

Tengo un modelo tabular en línea en el administrador de Django. Necesito 1 de los campos para no ser modificable después de que se haya creado, pero configurarlo como de solo lectura (a través de Readonly_fields) que funciona bien, pero convierte el campo en una etiqueta al hacer clic en "Agregar otro elemento" en lugar de un menú desplegable.Solo lectura para elementos existentes solo en Django admin inline

¿Hay alguna manera de mantener un campo de solo lectura, pero aún así permitir que se creen nuevos elementos con la entrada de campo adecuada?

Gracias!

Thomas

Editar: Managed que averiguarlo a través de un widget personalizado

class ReadOnlySelectWidget(forms.Select): 
    def render(self, name, value, attrs=None): 
     if value: 
      final_attrs = self.build_attrs(attrs, name=name) 
      output = u'<input value="%s" type="hidden" %s />' % (value, flatatt(final_attrs)) 
      return mark_safe(output + str(self.choices.queryset.get(id=value))) 
     else: 
      return super(ReadOnlySelectWidget, self).render(name, value, attrs) 

Simplemente lo convierte en un escondido si hay un valor, no va a funcionar en todas las situaciones (solo realmente funciona con 1 campo de solo lectura).

+1

añadir su solución como respuesta, así que la cuestión se presenta como respondida :) – ashwoods

Respuesta

0

Aquí hay una mejor sólo lectura widget que he usado antes:
https://bitbucket.org/stephrdev/django-readonlywidget/

from django_readonlywidget.widgets import ReadOnlyWidget 

class TestAdmin(admin.ModelAdmin): 
    def formfield_for_dbfield(self, db_field, **kwargs): 
     field = super(TestAdmin, self).formfield_for_dbfield(db_field, **kwargs) 
     if field: 
      field.widget = ReadOnlyWidget(db_field=db_field) 
     return field 
0

Esto es posible con un parche mono.

El siguiente ejemplo hará que el campo "nota" se lea solo para objetos AdminNote existentes. A diferencia de la conversión de campos para que se oculte como se sugiere en otras respuestas, esto realmente eliminará los campos del flujo de trabajo de envío/validación (que es más seguro y usa representadores de campo existentes).

# 
# app/models.py 
# 

class Order(models.Model): 
    pass 

class AdminNote(models.Model): 
    order = models.ForeignKey(Order) 
    time = models.DateTimeField(auto_now_add=True) 
    note = models.TextField() 


# 
# app/admin.py 
# 

import monkey_patches.admin_fieldset 

... 

class AdminNoteForm(forms.ModelForm): 
    class Meta: 
     model = AdminNote 

    def __init__(self, *args, **kwargs): 
     super().__init__(*args, **kwargs) 
     for field in self.get_readonly_fields(): 
      del self.fields[field] 

    def get_readonly_fields(self): 
     if self.instance.pk: 
      return ['note'] 
     return [] 


class AdminNoteInline(admin.TabularInline): 
    model = AdminNote 
    form = AdminNoteForm 
    extra = 1 
    fields = 'note', 'time' 
    readonly_fields = 'time', 


@admin.register(Order) 
class OrderAdmin(admin.ModelAdmin): 
    inlines = AdminNoteInline, 


# 
# monkey_patches/admin_fieldset.py 
# 

import django.contrib.admin.helpers 


class Fieldline(django.contrib.admin.helpers.Fieldline): 
    def __init__(self, *args, **kwargs): 
     super().__init__(*args, **kwargs) 
     if hasattr(self.form, 'get_readonly_fields'): 
      self.readonly_fields = list(self.readonly_fields) + list(self.form.get_readonly_fields()) 

django.contrib.admin.helpers.Fieldline = Fieldline 
+0

En el caso en el que he intentado esto me dio una ValueError durante la validación de formularios: 'No se puede asignar None:" MyModel.field "no permite valores nulos. – towr

27

teniendo el mismo problema, me encontré con esta revisión:

Cree dos objetos en línea, uno sin el permiso de cambio, y la otra con todos los campos de sólo lectura. Incluye ambos en el administrador modelo.

class SubscriptionInline(admin.TabularInline): 
    model = Subscription 
    extra = 0 
    readonly_fields = ['subscription', 'usedPtsStr', 'isActive', 'activationDate', 'purchaseDate'] 

    def has_add_permission(self, request): 
     return False 

class AddSupscriptionInline(admin.TabularInline): 
    model = Subscription 
    extra = 0 
    fields = ['subscription', 'usedPoints', 'isActive', 'activationDate', 'purchaseDate'] 

    def has_change_permission(self, request, obj=None): 
     return False 

incluirlos en el mismo modelo de administración:

class UserAdmin(admin.ModelAdmin): 
    inlines = [ AddSupscriptionInline, SubscriptionInline] 

Para añadir una nueva suscripción utilizo la línea de suscripción en el admin. Una vez que se guarda, la nueva suscripción desaparece de la línea, pero ahora aparece en la Suscripción en línea, como de solo lectura.

Para la suscripción en línea, es importante mencionar "extra = 0", por lo que no mostrará las suscripciones de solo lectura no deseado. También es mejor ocultar la opción de agregar para Suscripción en línea, para permitir la adición solo a través de Añadir Supscripción en línea, estableciendo el has_add_permission para que siempre devuelva False.

No es para nada perfecto, pero es la mejor opción para mí, ya que debo proporcionar la posibilidad de agregar suscripciones en la página de administración del usuario, pero después de agregarla, solo debe cambiarse mediante la lógica de la aplicación interna.

+2

Hice algo similar a esto, pero modifiqué el conjunto de consultas de Inline para nuevos registros (AddSubscriptionInline en el ejemplo): ' def queryset (self, request): return super (AddSubscriptionInline, self) .queryset (request) .none() ' y se agregó' max_num = 0' a Inline para registros existentes (SubscriptionInline en el ejemplo). – jenniwren

+0

Usé esta solución también. Funciona como magia. ¡Uno de los hacks más elegantes que he visto! –

+0

Hack Awsom que he visto en django admin. Muchas gracias !! –

1

De acuerdo con this post este problema ha sido reportado como un error en Ticket15602.

Una solución alternativa sería anular el método clean del modelo en línea en los formularios.py y elevar un error cuando se cambia de una línea existente:

class NoteForm(forms.ModelForm): 
    def clean(self): 
     if self.has_changed() and self.initial: 
      raise ValidationError(
       'You cannot change this inline', 
       code='Forbidden' 
      ) 
     return super().clean() 

    class Meta(object): 
     model = Note 
     fields='__all__' 
Cuestiones relacionadas