Wagtail: Limit choice dynamically based on current object - django

How can I limit the choices for the districtobject field in wagtail admin?
class DistrictPage(Page):
districtobject = models.ForeignKey(DistrictTranslated, on_delete=models.SET_NULL, null=True, blank=True)
I know that I can use "limit_choices_to" for basic limitations with Q. But I want to use a more dynamic approach which allows me to use the "content" of the current object. (Like self.attribute ... etc)
For example:
def my_limit_function(self):
1. get parent page
2. read date from parent page and extract this information for a filter query

I don't think you can do that in the Page class definition. I think you are going to need to customize the page form as in this example in the documentation. The values can be set in the form __init__.py. Don't forget that there won't be a parent page until your page is saved for the first time.

Related

How to query the database in Django forms?

I have a model:
class Registration(models.Model):
student_name = models.CharField(max_length=50)
season = models.ForeignKey(Season, on_delete=models.CASCADE)
subject = models.ForeignKey(Subject, on_delete=models.CASCADE)
address = models.TextField()
class Meta:
unique_together = (('student_name', 'selected_season', 'selected_subject'),)
I want to create a form for this model where the user will be given the option to select the subject based on the season that they selected. I have models for them as well:
class Subject(models.Model):
subject_name = models.CharField(max_length=50)
...
class Season(models.Model):
season = models.CharField(max_length=2, primary_key=True)
subject = models.ManyToManyField(Subject)
...
I dont know how to query the Form. Should It be a ModelForm or a regular Form? How to query the database in the form?
You can only know which season was selected when the form is submitted, so there's no simple direct way to implement this (note that this is a HTTP limitation, not a django one). IOW you'll need either a "wizard" process or front-end scripting.
The "wizard" solution is: one first form where the user selects the season, user submits the form, your code selects the related subjects and displays a second form with subjects choices, user selects subjects and submits for final validation (nb: this is usually done within a single view, using a form hidden field to keep track of the current step and which season was selected in first step). This is garanteed to work (if correctly implemented of course xD), but not really user friendly.
Second solution is to use front-end scripting.
In it's simplest form, when the user selects the season, you use js to hide other seasons subjects (or you first hide all subjects and only display relevant ones when the season is selected). This can be done rather simply by grouping all subjects for a given season in a same fieldset (or whatever other container tag) with an id matching the season's one, or by having a distinct html "select" (with same name but different ids) per season. Of course you can also have all subjects in one single html select (or whatever), keep a front-side (js) mapping of seasons=>subjects, and update your select or whatever from this mapping.
This solution can be made to work (in "degraded" mode) without JS (you just have to make sure the subjects selector(s) are visible and active by default). You'll have to implement a custom validation backend-side (cf django's forms doc for this) to make sure the subject matches the season anyway (never trust front-side validation, it's only user-friendly sugar), so in the worst case the form will just not validate.
Now if you have a LOT of seasons and subjects, you may want to prefer doing an ajax query when the user selects the season and populate the subjects selector from this query's result (to avoid initially rendering a huge list of options), but then you can't make it work without JS anymore (whether this is an issue or not depends on your requirements).
EDIT
If i do follow the form wizard option, I need to create 2 forms, the first consisting of just the Season.
Well, you could do it with one single form (passing an argument to specify what should be done) but using two forms is much simpler indeed.
The 2nd form will consist of the rest of the options (except for seasons), am I right? So should the 2nd form be a modelform?
Well, that's the point of modelforms, isn't it ?
How do I put a queryset in the ModelChoiceField in modelform? I googled but could't find anything –
In your form's __init__, you update self.fields["subject"].queryset with your filtered queryset.
IMPORTANT: do NOT try to touch self.fieldname - this is the class-level field definition, so changing it would totally unexpected results in production (been here, done that, and that was quite some fun to debug xD).
Now this being said, for your use case, I reckon you'd be better with the simple js filtering solution - it's the easiest to set up - on the backend it's totally transparent, and the front-end part is rather simple using jQuery -, it's much more user-friendly, and it's garanteed to work.

django manytoone other side

I have a django webapp. I have something like this set up:
class Doc(models.Model)
invoice = models.ForeignKey(Invoice, null=True, on_delete=models.SET_NULL, blank=True)
class Invoice(models.Model)
#bla bla bla
Can I somehow create a form element that would stand for invoice.doc_set.all() ? I want it to be I want it to be a multiple select element. I know I dont want Inline formsets. What I want to see in django admin on Invoice side --> simple Multiple select element with all the instances that can be selected. When selected and saved the particular invoice will become the foreign key instance to them...
You could use a ModelMultipleChoiceField.
https://docs.djangoproject.com/en/dev/ref/forms/fields/#modelmultiplechoicefield
But I don't think that the admin can manage this type of functionality out of the box. You will probably need to extends the admin form and set the FK by yourself.

Django admin site -- list ForeignKey items as change-list on change page

In (a toy version of) my project, there are Owners who own any number of Objects. My models.py file looks like
class Owner(models.Model)
name = models.CharField(max_length=50)
date_of_birth = models.DateField()
class Object(models.Model)
name = models.CharField(max_length=50)
price = models.models.DecimalField(max_digits=9, decimal_places=2)
owner = models.ForeignKey(Owner)
My question relates to the change page for an Owner on the admin site, e.g.
http://mysite.com/admin/myapp/owner/1/.
Now I know that if I register Object as a TabularInline or a StackedInline, then I get an editable list of the Objects this Owner owns. However, in the real version of my project, an Object has something like 25 fields, not 2, and so neither of those options is really desirable aesthetically.
What I would really like instead is to essentially have a change-list of all the Objects an Owner owns appear on the Owner's change-page. (That way I get a nice compact listing of all the Owner's Objects, and if I need to edit the details of one, I can click on its link and edit it in its own page.) Basically I want the contents of
http://mysite.com/admin/myapp/object/?owner__id__exact=1
to appear within
http://mysite.com/admin/myapp/owner/1/.
Is there a way to do this?
PS: I'm using Django 1.4 and Python 2.7.
You can define what form class and/or fields to use in each InlineModelAdmin using these attributes, and limit the amount of input fields per object that way.

limit choices for foreign field based on other field

Is it possible to do really dynamic form in AdminModel? I have following models:
class MyModel(models.Model):
firstfield=models.ForeignKey(First)
secondField= models.ForeignKey(Second, blank=True,null=True)
#some other fields
class Second(models.Model):
firstfield=models.ForeignKey(First)
#other fields
As you can see Second is optional. But I want it to limit according to current selection in First? It would require some page refreshing or some ajax work but I simply don't know how to even pass First value. Maybe I should add it to request and then use something similar to:
https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.formfield_for_foreignkey ?
You can do it through ajax request. If you don't know how it works see the below links.
How to implement two dropdowns dependent on each other using Django and jQuery
Dynamic select fields with JQuery and django

How would you populate a field based on another field

From the admin panel I want to populate a slug field based on a certain text field
eg.
Title: My Awesome Page
would automaticaly populate
Slug: my_awesome_page
There used to be a prepoulate_from option for the SlugField up to 0.96. It became an admin option after that. See here for reference on that.
Alternatively, you could override the model's save method to calculate the value of the slug field on save().
This question may be helpful, too.
There is also an app called django-autoslug which provides a field type AutoSlugField. Using that, you could have:
class Something(models.Model):
title = models.CharField(max_lenght=200)
slug = AutoSlugField(populate_from='title')
...
This AutoSlugField has many nice features, such as generating a slug so that it is unique either globally of combined with some other field (maybe a category or the year part of a DateTimeField).
See http://pypi.python.org/pypi/django-autoslug for further details.