Flask-admin UserWarning: Fields missing from ruleset - flask

When executing my Flask application I get the following warnings:
C:\Python27\lib\site-packages\flask_admin\model\base.py:1324: UserWarning: Fields missing from ruleset: password
warnings.warn(text)
C:\Python27\lib\site-packages\flask_admin\model\base.py:1324: UserWarning: Fields missing from ruleset: new_password
warnings.warn(text)
* Restarting with stat
C:\Python27\lib\site-packages\flask_admin\model\base.py:1324: UserWarning: Fields missing from ruleset: password
warnings.warn(text)
C:\Python27\lib\site-packages\flask_admin\model\base.py:1324: UserWarning: Fields missing from ruleset: new_password
warnings.warn(text)
I've normally used form_excluded_columns to remove unwanted fields from my form, but this time I have a hard time to get rid of those errors.
Here's my view:
class AdministratorView(sqla.ModelView):
page_size = 10
column_searchable_list = (
'username',
'description'
)
column_list = (
'username',
'apikey',
'description',
'active'
)
column_exclude_list = list = (
'apikey',
'source'
)
form_excluded_columns = (
'source',
'photos'
)
column_labels = {
'apikey': 'API Key'
}
form_widget_args = {
'apikey':{
'readonly':True
}
}
form_create_rules = (
rules.FieldSet(('username', 'password', 'description'), 'Personal'),
rules.FieldSet(('roles', 'apikey', 'active'), 'Permission'),
)
form_edit_rules = (
rules.FieldSet(('username', 'description'), 'Personal'),
rules.FieldSet(('roles', 'apikey', 'active'), 'Permission'),
rules.Header('Reset password'),
rules.Field('new_password')
)
def on_model_change(self, form, model, is_created):
if is_created is False:
if form.new_password.data:
model.password = generate_password_hash(form.new_password.data)
def scaffold_form(self):
form_class = super(AdministratorView, self).scaffold_form()
form_class.password = fields.PasswordField('Password', [validators.Required()])
form_class.new_password = fields.PasswordField('New Password')
return form_class
def is_accessible(self):
if login.current_user.is_authenticated:
return login.current_user.has_role('admin')
The purpose of this view is to have a single, required Password-field on the create form and a optional "New password"-field on the edit-form. I understand that the warnings arise when I don't include password/new_password in form_create_rules and form_edit_rules, but adding those fields to form_excluded_columns doesn't fix it.
Any tips on how I can get rid of the warnings?
Edit:
I suppose I should rather use get_create_form and get_edit_form instead of only scaffold_form. One benefit is that this makes it easier to override each form separately. Can I simplify this further? Should I do requirement validation like this or add nullable=False to the database schema (SQLAlchemy)?
class AdministratorView(sqla.ModelView):
page_size = 10
column_searchable_list = (
'username',
'description'
)
column_list = (
'username',
'apikey',
'description',
'active'
)
column_exclude_list = list = (
'apikey',
'source'
)
form_excluded_columns = (
'source',
'photos'
)
column_labels = {
'apikey': 'API Key'
}
form_widget_args = {
'apikey':{
'readonly':True
}
}
form_create_rules = (
rules.FieldSet(('username', 'password', 'description'), 'Personal'),
rules.FieldSet(('roles', 'apikey', 'active'), 'Permission'),
)
form_edit_rules = (
rules.FieldSet(('username', 'description'), 'Personal'),
rules.FieldSet(('roles', 'apikey', 'active'), 'Permission'),
rules.Header('Reset password'),
rules.Field('new_password')
)
def get_create_form(self):
form = self.scaffold_form()
form.username = fields.StringField('Username', [validators.Required()])
form.password = fields.PasswordField('Password', [validators.Required()])
return form
def get_edit_form(self):
form = self.scaffold_form()
delattr(form, 'password')
form.new_password = fields.PasswordField('New Password')
return form
def on_model_change(self, form, model, is_created):
if is_created is False:
if form.new_password.data:
model.password = generate_password_hash(form.new_password.data)
def is_accessible(self):
if login.current_user.is_authenticated:
return login.current_user.has_role('admin')

Perhaps this will help: How can I avoid Flask-Admin 2.1 warning "UserWarning: Fields missing from ruleset"?
Here's the relevant code from that answer:
import warnings
with warnings.catch_warnings():
warnings.filterwarnings('ignore', 'Fields missing from ruleset', UserWarning)
admin.add_view(UserView())

Related

Create view with more models

hi everyone I have a doubt with the use of forms and models.
I have to create a code that creates records in multiple tables and I don't know how to do it.
my goal is to create a page where I can enter all the data and when I save it creates the various tables filled in with the data provided by the user.
I'm a beginner I still have to learn the mechanism well =)
forms.py
from django import forms
from .models import Schede, DatiGruppi, Gruppi
class CreaSchedaForm(forms.ModelForm):
nome_scheda = forms.CharField(
required = True,
label ='Nome scheda',
widget = forms.TextInput(
attrs = {
'class': 'form-control',
'placeholder' : 'nome scheda',
'autocomplete' : 'off'
}
)
)
data_inizio = forms.DateField(
label='Data inizio',
widget = forms.DateInput(
attrs= {
'type': 'date',
'class': 'form-control',
'placeholder' : 'data inizio'
}
)
)
data_fine = forms.DateField(
label='Data fine',
widget = forms.DateInput(
attrs= {
'type': 'date',
'class': 'form-control',
'placeholder' : 'data fine'
}
)
)
class Meta:
model = Schede
fields = ['nome_scheda','data_inizio','data_fine']
class CreaDtGruppoForm(forms.ModelForm):
giorni_settimana = forms.ChoiceField(
choices = DatiGruppi.giorni_settimana_scelta
)
dati_gruppo = forms.ModelChoiceField(
queryset = Gruppi.objects.all(),
empty_label = "-",
required = True
)
class Meta:
model = DatiGruppi
fields = ['giorni_settimana', 'dati_gruppo']
views.py
#login_required
def creaScheda(request):
if request.method == "POST":
form = CreaSchedaForm(request.POST)
if form.is_valid():
scheda = form.save(commit = False)
scheda.utente = request.user
scheda.save()
else:
form = CreaSchedaForm()
context = {"form": form}
return render(request, "crea_scheda.html", context)

How to load choices for one of form_extra_fields in ModelView dynamically during serving data for rendering?

I have an User admin ModelView with form_extra_field constituency and I wanted to have it dynamically loaded choices (it is SelectField). Model for this view is User which will store constituency as id of the item.
I tried to load choices from remote API but certainly it was wrong time to load these things because of RuntimeError: Working outside of application context. It happened at migration command ...venv/bin/flask db init -d ctiweb/migrations . I read some API_KEY from config object which needs to have application context ...
So solution would be load choices at serving or rendering time. I don't want have the solutiont in customized template rather I want it in customized admin ModelView for User model.
Is there such a solution? Can I overload somemethod of ModelView which has application context already (so it is during serving data or rendering template)?
part of source code from models.py
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
password_hash = db.Column(db.String(128))
email = db.Column(db.String(255), nullable=False)
is_superuser = db.Column(db.Boolean())
constituency_id = db.Column(db.Integer())
def __str__(self):
return self.username
#property
def password(self):
raise AttributeError("not readable attribute")
#password.setter
def password(self, password):
self.password_hash = generate_password_hash(password)
def verify_password(self, password):
return check_password_hash(self.password_hash, password)
part of source code from admin.py
from flask_admin.contrib.sqla import ModelView
class UserAdminModelView(ModelView):
def serve_constituency_choices():
consituencies = (
get_constituency(constituency_id) for constituency_id in get_constituencies()
)
return ((constituency['id'], constituency['name']) for constituency in consituencies)
column_list = ['username', 'email', 'is_superuser']
form_columns = ['username', 'email', 'password', 'is_superuser', 'constituency']
form_extra_fields = {
'password': PasswordField('Password'),
'constituency': SelectField('Constituency', choices=serve_constituency_choices())
}
There are methods on ModelView - create_form and edit_form, that can be overloaded and return form with right choices loaded from remote API. Look at the documentation https://flask-admin.readthedocs.io/en/latest/api/mod_model/#flask_admin.model.BaseModelView.create_form and https://flask-admin.readthedocs.io/en/latest/api/mod_model/#flask_admin.model.BaseModelView.edit_form .
class UserAdminModelView(ModelView):
column_list = ['username', 'email', 'is_superuser']
form_columns = ['username', 'email', 'password', 'is_superuser', 'constituency']
form_extra_fields = {
'password': PasswordField('Password'),
'constituency': SelectField('Constituency', coerce=int)
}
class ConstituencyChoices(Iterator):
def __init__(self, constituencies, first_item=None):
self.first_item = first_item
self.constituencies = constituencies
def __next__(self):
if self.first_item is not None:
res = self.first_item
self.first_item = None
return res
constituency = self.constituencies.__next__()
res = (constituency['id'], constituency['name'])
return res
def serve_constituency_choices(self):
constituencies = (
get_constituency(constituency_id) for constituency_id in get_constituencies()
)
return self.ConstituencyChoices(constituencies, (0, "-----"))
def create_form(self, obj=None):
form = super().create_form(obj=obj)
form.constituency.choices = self.serve_constituency_choices()
return form
def edit_form(self, obj=None):
form = super().edit_form(obj=obj)
form.constituency.choices = self.serve_constituency_choices()
return form
Actually I have finally find a simpler solution - only with class iterator on constituency choices. I think is better than the first one :-)
class ConstituencyChoices(Iterator):
def __init__(self, first_item=None):
self.first_item = first_item
self.constituencies = None
def __next__(self):
if self.first_item is not None:
res = self.first_item
self.first_item = None
return res
if self.constituencies is None:
self.constituencies = (
get_constituency(constituency_id) for constituency_id in get_constituencies()
)
constituency = self.constituencies.__next__()
res = (constituency['id'], constituency['name'])
return res
# pylint: disable=too-many-ancestors
class UserAdminModelView(ModelView):
column_list = ['username', 'email', 'is_superuser']
form_columns = ['username', 'email', 'password', 'is_superuser', 'constituency']
form_extra_fields = {
'password': PasswordField('Password'),
'constituency': SelectField(
'Constituency',
coerce=int,
choices=ConstituencyChoices(
(0, "--- {} ---".format(_('no constituency')))
)
)
}

AttributeError: 'ModelFormOptions' object has no attribute 'private_fields'

I get this error when trying to create a custom Form on the Admin view, I don't see any solution on stackoverflow (yes similar problems) so I think it's a good idea to post it:
My Models.py:
class Webhook(models.Model):
url = models.CharField('URL', max_length = 60, unique = True)
description = models.CharField('Descripcion', max_length = 255)
enabled = models.BooleanField('Enabled', default=True)
event = models.CharField('Evento', max_length=1, choices=Events.EVENTS_CHOICES)
My Forms:
class WebhookForm(forms.ModelForm):
class Meta:
model = Webhook
fields = '__all__'
def save(self, commit=True):
print('saveeeeeee')
webhook = super().save(commit=False)
webhook.code = webhook.id
# Get token
response_token = TOKEN.get()
if response_token['success']:
# Create Webhook
url = 'https://sandbox.bind.com.ar/v1/webhooks'
headers = {
'Content-type': 'application/json',
'Authorization': 'JWT ' + response_token['token']
}
data = {
'url': webhook.url, # Debera responder status_code == 200
'description': webhook.description,
'code': webhook.id,
'enabled': webhook.enabled,
'events': webhook.event
}
data_json = json.dumps(data)
response = requests.put(url, data= data_json, headers = headers)
response_json = response.json()
# Result
result = {}
if response.status_code == 409:
result['success'] = False
result['details'] = response_json
else:
result['success'] = True
result['data'] = response_json
# If ok, Save
if commit & result['success']:
webhook.save()
return result['success']
My Admin.py
class WebhookAdmin(forms.ModelForm): # Only to override a form in admin
class Meta:
model = WebhookForm
fields = '__all__'
# Style to add a new User
add_form = WebhookForm
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('url', 'description', 'code', 'enabled', 'events',)}
),
)
# Style to edit a new User
form = WebhookForm
fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('url', 'description', 'code', 'enabled', 'events',)}
),
)
admin.site.register(Webhook, WebhookAdmin)
And I get this error when I try to do python manage.py makemigrations:
AttributeError: 'ModelFormOptions' object has no attribute 'private_fields'
You have subclassed the wrong thing. The admin class needs to inherit from admin.ModelAdmin, not forms.ModelForm.
ModelAdmin classes don't have an inner Meta class. You need to remove that class altogether.
The Attribute error was because I was trying to assign a Form into the class Meta, and it needs a Model:
class Meta:
model = WebhookForm
fields = '__all__'

Django 2.0.7. is missing the complete path in url when the user try to upload a file. It works on local enviroment but not in production server

I have a model with a ImageField:
class Usr( User ):
idusuario = models.AutoField( primary_key = True )
usuario = models.CharField( blank = False, unique = True, max_length = 50 )
contraseña = models.CharField( blank = False, max_length = 250 )
telefono = models.CharField( max_length = 15, blank = True, null = True )
celular = models.CharField( max_length = 15, blank = True, null = True )
fotografia = models.ImageField( blank = True, null = True, upload_to = 'usuarios' )
depende_de = models.ForeignKey( 'self', on_delete = models.CASCADE, related_name = '+', blank = True, null = True )
def __unicode__( self ):
return self.get_full_name()
def __str__( self ):
return self.get_full_name()
And its form (a ModelForm ):
class RegUsuario( forms.ModelForm ):
class Meta:
model = Usr
fields = [
'usuario',
'contraseña',
'is_active',
'is_superuser',
'first_name',
'last_name',
'email',
'telefono',
'celular',
'fotografia',
'groups',
'depende_de'
]
In views (vw_usuario.py) I have:
#valida_acceso( [ 'usr.agregar_usuarios_usuario' ] )
def new( request ):
if 'POST' == request.method:
frm = RegUsuario( request.POST, files = request.FILES )
if frm.is_valid():
obj = frm.save( commit = False )
obj.username = obj.usuario
obj.set_password( obj.contraseña )
obj.save()
for g in request.POST.getlist( 'groups' ):
obj.groups.add( g )
obj.save()
return HttpResponseRedirect( reverse( 'usuario_ver', kwargs = { 'pk' : obj.pk } ) )
frm = RegUsuario( request.POST or None )
return render( request, 'global/form.html', {
'menu_main' : Usr.objects.filter( id = request.user.pk )[ 0 ].main_menu_struct(),
'footer' : True,
'titulo' : 'Usuarios',
'titulo_descripcion' : 'Nuevo',
'frm' : frm
} )
And in urls.py:
urlpatterns = [
...
path( 'usuarios/nuevo/', vw_usuario.new, name = "usuario_nuevo" ),
...
]
If I go to http://example.com/usuarios/nuevo/ in the browser I can the form displayed as well, then I fill the fields and submit the form, and I got the exception:
Page not found (404)
Request Method: POST
Request URL: http://example.com/usuarios/nuevo/
Raised by: seguridad.mkitsafe.validacion
Using the URLconf defined in example.urls, Django tried these URL patterns, in this order:
...
usuarios/eliminar/<pk>/ [name= usuario_eliminar ]
usuarios/nuevo/ [name= usuario_nuevo ]
usuarios/<pk>/ [name= usuario_ver ]
...
The current path, nuevo/, didn t match any of these.
I don't understand why the url is taken as nuevo/ instead of usuarios/nuevo/
In the form template I'm setting method = "post", enctype="multipart/form-data" and action="" (I've also tried action="usuarios/nuevo" and action="http://example.com/usuarios/nuevo/")
The path for MEDIA_ROOT exists and has enough perms.
The issue happens if I fill in the form the ImageField field or not.
I noticed that if I remove the field in the modelform, everything works as well.
The source code for seguridad.mkitsafe.validacion is:
def valida_acceso( permisos = None ):
url_error = 'seguridad_inicio'
url_autenticacion = 'seguridad_login'
def _valida_acceso( vista ):
def validacion( *args, **kwargs ):
usuario = args[ 0 ].user
if not usuario.is_authenticated:
print_error( "Vista {} negada por autenticación".format( vista.__name__ ), "Exec Info" )
return HttpResponseRedirect( reverse( url_autenticacion ) )
if permisos is None:
return vista( *args, **kwargs )
perms = []
for perm in permisos:
permiso = Permiso.get_from_package_codename( perm )
if( permiso is None ):
print_error( "No se ha encontrado el permiso: " + perm )
else:
perms.append( permiso.perm() )
desc = permiso.descendencia()
for p in desc:
perms.append( p.perm() )
for perm in perms:
p = "{}.{}".format( perm.content_type.app_label, perm.codename )
if usuario.has_perm( p ):
return vista( *args, **kwargs )
print_error( "Vista {} negada por permisos {}".format( vista.__name__, permisos ), "Exec Info" )
return HttpResponseRedirect( reverse( url_error ) )
return validacion
return _valida_acceso
And it is a decorator which verify perms and authentication.
If I remove this decorator the excepion changes in the Raised by to seguridad.vw_usuario.new
Page not found (404)
Request Method: POST
Request URL: http://example.com/usuarios/nuevo/
Raised by: seguridad.vw_usuario.new
Using the URLconf defined in example.urls, Django tried these URL patterns, in this order:
admin/
...
usuarios/actualizar/<pk>/ [name='usuario_actualizar']
usuarios/eliminar/<pk>/ [name='usuario_eliminar']
usuarios/nuevo/ [name='usuario_nuevo']
usuarios/<pk>/ [name='usuario_ver']
usuarios/ [name='usuario_inicio']
...
The current path, nuevo/, didn't match any of these.
I also noticed that when the error is raised and the decorator is active the decorator or the view function is not called.
I also tried to copy a file in MEDIA_ROOT path with an script and it worked as well, so the script has right access to this path.
Any comment about the issue?

return user id django

I have a model field
account_handler = models.ForeignKey(User, blank=True, null=True, related_name='handling_leads', on_delete=models.SET_NULL)
Currently I'm doing something like this
def get_queryset(self):
user_id = User.objects.all()
queryset = User.objects.filter(handling_leads__account_handler=user_id).distinct()
return queryset
and I've got some results for testing purposes. Now the question is, how can I properly use this field and return a user_id? I have tried something like this
User.objects.filter(handling_leads__account_handler=self.request.user.id).distinct()
with out User.objects.all() but that was just returning an empty queryset. I'm trying to filter this so I can populate a table with user__first_name and user__last_name, I need user_id so I can redirect them to their individual page.
controler for the datatable:
app = angular.module 'statistics'
app.controller 'SalesCommissionsListCtrl', ['$scope', '$compile',
($scope, $compile) ->
$scope.init = ()->
fetchCommissionsList()
fetchCommissionsList = ()->
$('#commissionsSalesList').DataTable({
createdRow: (row, data, index) ->
$compile(row)($scope)
sDom: 'lftip',
processing: true,
serverSide:true,
searchDelay: 1000,
orderMulti: false,
pageLength: 10,
ajax: {
url: '/api/statistics/commissions/list/',
data: (data)->
data.limit = data.length;
data.offset = data.start;
data.search = data.search['value'];
if (data.order[0]['dir'] == 'asc')
data.ordering = data.columns[data.order[0]['column']].name;
else
data.ordering = '-' + data.columns[data.order[0]['column']].name;
return 0
dataFilter: (data) ->
json = jQuery.parseJSON(data);
json.recordsTotal = json.count;
json.recordsFiltered = json.count;
json.data = json.results;
return JSON.stringify(json);
}
columns: [
{
data: "username",
name: "username__first_name, username__last_name",
render: (data, type, row, meta)->
return '' + data + '';
}
]
})
]
my ModelViewSet:
from django.contrib.auth.models import User
from rest_framework import viewsets, filters, permissions
from app.restapi.pagination import StandardResultsOffsetPagination
from statistics.restapi.serializers import SalesCommissionsSerializer
from statistics.restapi.permissions import SalesCommissionsListPermissions
class SalesCommissionsViewSet(viewsets.ModelViewSet):
def get_queryset(self):
user_id = User.objects.all()
queryset = User.objects.filter(handling_leads__account_handler=user_id).distinct()
return queryset
serializer_class = SalesCommissionsSerializer
filter_backends = (filters.DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter)
filter_fields = (
'id', 'username', 'first_name', 'last_name'
)
ordering_fields = (
'id', 'username', 'first_name', 'last_name',
)
search_fields = (
'id', 'username', 'first_name', 'last_name',
)
pagination_class = StandardResultsOffsetPagination
permission_classes = [permissions.IsAuthenticated, SalesCommissionsListPermissions]
serializer:
from django.contrib.auth.models import User
from rest_framework import serializers
class SalesCommissionsSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'first_name', 'last_name')
If you want to filter for users who are 'handling leads', you can do the following:
handling_leads = User.objects.filter(handling_leads__isnull=False)
Here's a link to the documentation: https://docs.djangoproject.com/en/1.10/ref/models/querysets/#std:fieldlookup-isnull