How would I be able to override and add some extra code when creating a new row in a table via the Admin panel in Flask?
For example: User enters info for a new row in the 'Post' table and clicks save. I want to add some code to automate a process with that new row information.
You can override the methods on_model_change to perform actions before saving/updating a new model, or after_model_change to do something after, obviously.
You can inherit from the class BaseModelView or ModelView if you are using Flask-SqlAchemy.
In every cases, 3 arguments are provided to play with : the form used by the view, the new/updated model and the flag is_created to know if the model is new (True) or updated.
You can defined the model view like below :
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView
class PostView(ModelView):
def after_model_change(self, form, model, is_created):
print(form, model, is_created)
def on_model_change(self, form, model, is_created):
print(form, model, is_created)
admin = Admin(template_mode='bootstrap3')
admin.add_view(PostView(Post, db.session, name='Posts'))
Note : You have access to similar methods for the deleting part with on_model_delete and after_model_delete, except you only have the model given as argument.
Related
I have a custom user model (CustomUsers) on one app called users, and then I have a model called Bookings from which I have created a ModelfForm called BookingsForm.
In the Bookings model (and ultimately the BookingsForm), I have a field called booking_author which has a ForeignKey inherited from the CustomUsers model.
Now I have been able to successfully call the booking_author field into my bookingscreate view and make it uneditable/read only as I had wanted. The issue now is that the field is displaying the id of the author instead of the name of the author. Is anyone able to help me resolve this?
views.py
#login_required
def bookingscreate(request):
bookings_form = BookingsForm(initial={'booking_author': request.user })
context = {
'bookings_form': bookings_form
}
return render(request, 'bookings_create.html', context)
There are multiple ways of getting the users name. To obtain the user name in this case you could use
request.user.get_full_name()
request.user.get_short_name()
Explanation:
request.user is the User object, and thus you have access to all user methods. If the above is not what you're looking for you could create a method in your user class and then call that method in this view.
I'm currently trying to create a custom ModelView for a detail_view using a template on Flask Admin. However I'm struggling to figure out how to access the instance of a model that the user is viewing the details of.
This is my custom ModelView:
class ShopModelView(MyModelView):
can_view_details = True
details_template = "custom_detail_view.html"
#expose('/details/', methods=('GET', 'POST'))
def details_view(self):
self._template_args['all_transactions'] = #current_shop_object#.transactions.order_by(Transaction.timestamp.desc())
return super(ShopModelView, self).details_view()
Looking at this post, the class of the model can be obtained using self.model, however this returns the class rather than the instance of the specific model being accessed.
The documentation on Templates and ModelView doesn't seem to explain it.
How can I get the instance of the specific model being accessed?
Turns out that the current instance of the model can be accessed via the variable model inside the jinja2 template file. So instead of parsing the variable as a template argument like I was trying to: self._template_args['all_transactions'] = #current_shop_object#.transactions.order_by(Transaction.timestamp.desc()),
{{ model.transactions.all() }} achieves the result I was aiming for.
i have a Django project and right now everything works fine. i have a Django admin site and now, i want that when i add a new record to my model, a function calls simultaneously and a process starts. how i can do this? what is this actions name?
1 WAY
You can go to your models.py into your app by using django signal you can do this.
from django.db.models.signals import post_save
class Test(models.Model):
# ... fields here
# method for updating
def update_on_test(sender, instance, **kwargs):
# custome operation as you want to perform
# register the signal
post_save.connect(update_on_test, sender=Test)
2 WAY
You can ovveride save() method of modeladmin class if you are filling data into table by using django admin.
class TestAdmin( admin.ModelAdmin ):
fields = ['title', 'body' ]
form = TestForm
def save_model(self, request, obj, form, change):
# your login if you want to perform some comutation on save
# it will help you if you need request into your work
obj.save()
Let's assume that I have following models:
class ScoutBook(models.Model):
troop = models.ForeignKey('Dictionary', limit_choices_to={'type' : 'Troop'}, related_name='+', blank=True, null=True)
class Dictionary(models.Model):
name = models.CharField(max_length=CHAR_FIELD_MAX_LEN, verbose_name="Nazwa")
active = models.BooleanField(verbose_name="Aktywny")
type = models.CharField(max_length=CHAR_FIELD_MAX_LEN, choices=DICTIONARY_CHOICES)
and I want to implement following logic:
when creating ScoutBook allow users to select only active troops, and when editing allow to select active troops or allow user to leave value unchanged (even if the troop is inactive). If I use limit_choices_to = {..., 'active' = True} troop that is inactive is absent from combo box in django admin.
So to be clear: let's assume that there are four troops in this system: Troop1, Troop2 and InactiveTroop, InactiveTroop2. On model creation I would like user to be able to choose Troop1 and Troop2. If model has troop field set to InactiveTroop2, I would like user to be able to choose between InactiveTroop2, Troop1 and Troop2.
I was looking at the django forms api and I didn't found obvious way do this. Moreover, in the application I'm developing there will be many such fields and many such models --- so solution must be pain free. I would rather not create new Form class for every model. I will be using mostly django admin to enable editing the database, and some read only views that will just list entries.
Ideally I would like to encapsulate this functionality in some custom field --- but fields have access to model instance on validate and save phase --- so I dont have access to it when I produce formfield.
This sounds like something you want to do in a form, not in the object itself. Create a ModelForm and override the ModelChoiceField like this:
from django import forms
class ScoutBookForm(forms.ModelForm):
troop = forms.ModelChoiceField(queryset=Troop.objects.filter(active=True))
class Meta:
model = ScoutBook
You can also override the clean method of ScoutBook to ensure it cannot ever be saved with an inactive Troop, though that may have some unintended consequences (e.g., you wouldn't be able to update a ScoutBook in the admin if the troop had gone inactive at some point in the past).
Well I had to hook into ModelForm creation. Attached Form inspects it's fields and if specific conditions are met it replaces model field queryset.
class DictionayModelForm(ModelForm):
def __init__(self, *largs, **kwargs):
super(DictionayModelForm, self).__init__(*largs, **kwargs)
if self.instance and self.instance.pk is not None:
for f in self.instance._meta.fields:
if isinstance(f, models.ForeignKey) and issubclass(f.rel.to, Dictionary):
model_field = self.fields[f.name]
value = getattr(self.instance, f.name, None)
if value and value not in model_field.choices:
model_field.queryset = Dictionary.objects.filter(Q(**f.rel.limit_choices_to) | Q(id = value.id))
I am using FormWizard to complete a set of operation in my app, I have two models Employee and Person, Employee class inherits Person, and all the fields of Person are available for Employee object.
Now I am creating a set of forms using FormWizard, I just wanted to know that. If a user starts entering the data in the forms and fills upto 2 forms out of 4 and is willing to fill the rest of the forms afterwards. So is this possible that the data for the two forms which he filled can be saved in the database.
And the next time he comes can complete the operation form the 3rd form.
If anyone knows that then plz help me out, it would be a great help. Thank You!
what you can do is every step, save out the form state to some serialised object in db ForeignKeyed to the user.
then when hooking up the formwizard, wrap the formwizard view in a custom view which checks if the user has a saved form and if so deserialises and redirects to the appropriate step.
Edit: seems formwizard saves state in POST. only need to save postdata.
models.py:
class SavedForm(Model):
user = ForeignKey(User)
postdata = TextField()
views.py:
import pickle
class MyWizard(FormWizard):
def done(self, request, form_list):
SavedForm.objects.get(user=request.user).delete() # clear state!!
return render_to_response('done.html',)
formwizard = MyWizard([Form1, Form2]) <- class name, not instance name
def formwizard_proxy(request, step):
if not request.POST: #if first visit, get stored data
try:
prev_data = SavedForm.objects.get(user=request.user)
request.POST = pickle.loads(prev_data.postdata)
except:
pass
else: # otherwise save statet:
try:
data = SavedForm.objects.get(user=request.user)
except:
data = SavedForm(user=request.user)
data.postdata=pickle.dumps(request.POST)
data.save()
return formwizard(request)
edit: changed formwizard constructor