tutorial tipos relaciones pagina page modelos landing inicio entre create campos django django-admin

tipos - relaciones entre modelos django



Solo lectura para artículos existentes solo en Django admin en línea (5)

Aquí hay un mejor widget de solo lectura 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

Tengo un modelo tabular en línea en el administrador de Django. Necesito que 1 de los campos no se pueda cambiar después de que se haya creado, pero configurándolo 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.

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

¡Gracias!

Thomas

Edición: Gestionado para resolverlo 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)

Solo lo convierte en un valor oculto si hay un valor, no funcionará en todas las situaciones (solo funciona realmente con 1 campo de solo lectura).


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

Una solución alternativa sería anular el método de clean del modelo en línea en forms.py y generar un error cuando se cambia una en 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__''

Lo anterior da una solución a nivel de modelo.

Para generar un error cuando se cambia un campo específico, el clean_<field> puede ayudar. Por ejemplo, si el campo es un ForeignKey llamado category :

class MyModelForm(forms.Form): pass # Several lines of code here for the needs of the Model Form # The following form will be called from the admin inline class only class MyModelInlineForm(MyModelForm): def clean_category(self): category = self.cleaned_data.get(''category'', None) initial_value = getattr( self.fields.get(''category'', None), ''initial'', None ) if all( ( self.has_changed(), category.id != initial_value, ) ): raise forms.ValidationError( _(''You cannot change this''), code=''Forbidden'' ) return category class Meta: # Copy here the Meta class of the parent model


De hecho, me encontré con otra solución que parece funcionar muy bien (no puedo reconocerlo, pero enlace aquí ).

Puede definir el método get_readonly_fields en su TabularInline y configurar los campos de solo lectura adecuadamente cuando hay un objeto (edición) frente a cuando no hay uno (creación).

def get_readonly_fields(self, request, obj=None): if obj is not None: # You may have to check some other attrs as well # Editing an object return (''field_name'', ) else: # Creating a new object return ()

Esto tiene el efecto de hacer que su campo de destino sea de solo lectura cuando está editando una instancia de salida, al tiempo que permite que sea editable al crear una nueva instancia.

Como se señala a continuación en el comentario, esto no funciona como se esperaba porque el objetivo que se aprobó es en realidad el principal ... Hay un antiguo ticket de django que trata esto Ticket15602 .


Esto es posible con un parche de mono.

El siguiente ejemplo hará que el campo "nota" se lea solo para los objetos AdminNote existentes. A diferencia de los campos de conversión que se ocultan, como se sugiere en otras respuestas, esto eliminará los campos del flujo de trabajo de envío / validación (que es más seguro y utiliza 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


Teniendo el mismo problema, me encontré con esta solución:

Cree dos objetos en línea, uno sin permiso de cambio y el otro con todos los campos de solo lectura. Incluir ambos en el modelo de administrador.

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

Inclúyelos en el mismo modelo de administrador:

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

Para agregar una nueva suscripción, uso el AddSubscriptionInline en el administrador. Una vez que se guarda, la nueva suscripción desaparece de esa línea, pero ahora aparece en la SubscriptionInline , como solo lectura.

Para SubscriptionInline , es importante mencionar extra = 0 , por lo que no mostrará suscripciones de solo lectura no deseadas. También es mejor ocultar la opción de agregar para SubscriptionInline , para permitir agregar solo a través de AddSubscriptionInline , configurando el has_add_permission para devolver siempre False .

No es perfecto en absoluto, pero es la mejor opción para mí, ya que debo proporcionar la posibilidad de agregar suscripciones en la página de administración de usuarios, pero después de agregar una, solo se debe cambiar a través de la lógica interna de la aplicación.