django admin inlines reordering read only fields - django-admin

I am using tabular inlines in the django admin and have specified two fields to be read only. However django has now moved these to the very end of the inline (on the far right) There are a large number of fields, how can I move these 2 fields back to their original position (2nd, 3rd after pk) without having to specify the order of every field with fields=?

Use the get_fields() method of the ModelAdmin class:
#admin.register(Vintner)
class VintnerAdmin(admin.ModelAdmin):
exclude = ['field1', 'field2']
def get_fields(self, request, obj=None):
fields = super().get_fields(request, obj)
fields = fields[-2:] + fields[:-2] # or something more robust
return fields

Related

Processing multiple controller parameters into a single model attribute?

EDIT
It turns out I misunderstood how the #date_select form helper method works — I arrived at the approach below based on this answer, but after some tinkering, I discovered that, in typical Rails fashion, #date_select actually handles the whole thing automagically.
In any case, there are still times when I'd like to do something similar: for instance, if I want to capture a single User.tenure attribute as a serialized hash ({ start: <year>, end: <year> }), and I implement this in the view form as two separate fields that return the parameters :tenure_start and :tenure_end.
Where would the logic go to convert these two parameters into a single model attribute?
My problem, specifically
I want to have a "date-of-birth" attribute for the User model in my application. The New User form uses Rails' built-in #date_select form helper method.
In the view, #date_select creates three separate drop-down menus for year, month, and day. In the controller, it returns three separate parameters: dob(1i), dob(2i), and dob(3i). Each of these parameters is a string representation of its respective field (e.g., if you select 1992/7/14, the parameter values are "1992", "07", and "14").
I want to convert these three string parameters into a single Date object and assign it to a new User.
The question, generally
My understanding of MVC is that the controller should be thin: its job is merely to pass information between the model and the view. But in the above case, there are controller parameters that don't have corresponding model attributes. Thus,
User.create(user_params)
doesn't work, because User::new doesn't accept the :"dob(1i)" attribute (and wouldn't know what to do with it if it did). They need to be processed into a single value with something like
dob = params.fetch(:player).values_at(%i(dob(1i) dob(2i) dob(3i))).map(&:to_i)
params.fetch(:player)[:dob] = { player: { dob: Date.new(dob[0], dob[1], dob[2]) } }
before they can be saved into the dob attribute.
My question is this: How much of this logic belongs in the controller, and how much belongs in the model?
Should the controller be left to handle conversion of the input, and instantiate the model with exactly the data it needs?
or should the controller simply concatenate the three fields into a single string (e.g., "19920714") and leave it to the model to convert it to a Date before saving?
or is there a way to pass the three raw, unprocessed parameters into a model object and have it handle the conversion?
or is there another possibility I haven't thought of?
You can do something like this to store actual date in database
year = "1992"
month = "14"
day = "07"
dob = m + '/' + d + '/' + y
=> Date.parse(dob)
=> Tue, 14 Jul 1992

Django InLineSetModel and many2many field choices filtering

I have a many2many field in my model and I'm trying to render it in django admin using the inlineform. Everything is fine, but I can't customize the form as I want.
The model is simple there is a Supplier and a manytomany field named locations
Supplier(models.Model):
location = models.ManyToManyfield(Location, blank=True)
the inlineform
class LocationInLineForm(admin.TabularInLine):
model = Supplier.Location.through
The first thing I want to is to limit the choices. The constrains are two:
The location model has a boolean field 'active' and I need to filter based on that
The Supplier must be the Supplier selected in the django admin.
The admin user load the django admin, click on the supplier and the InLineForm must show his active location.
I thought that the following method is the right one to ovverride:
def formfield_for_manytomany(self, db_field, request, **kwargs):
but is not called by the InLineForm. The on called is:
def formfield_for_foreignkey(self, db_field, request, **kwargs):
The example in the django docs are based on the request.user but what I need is the current supplier where I can get it? In the request? But where? request.GET is empty. Maybe I can get it from db_field, but I don't know how.
If I can get the supplier id, I can create a queryset and pass it using kwargs['queryset']. Am I right?

How to set initial value for read only field

I have a django application which is using django.contrib.admin for administrative tasks.
For one model I now need to add a field which indicates which part of the code each row was created from. I am using readonly_fields to prevent this value from being changed through the administration interface.
A default value in this field will tell me that the row was either
created before the field was introduced
created by code which has not been updated to set the field
created through the administration interface
But I need better granularity than that. In particular I want to be able to distinguish between a row created by code which doesn't know about the field, and a row created through the administration interface.
Is there some way my ModelAdmin class can specify an initial value for a field mentioned in readonly_fields?
One way to do this is,
def get_form(self, request, caja=None, **kwargs):
self.form = YourModelForm
form = super(YourModelAdmin, self).get_form(request, caja, **kwargs)
form.base_fields['field'].initial = your_initial_data
return form
I found this solution, which appears to work:
class Admin(ModelAdmin):
readonly_fields = ('created_by',)
def save_form(self, request, form, change):
r = super(Admin, self).save_form(request, form, change)
if not change:
assert r.created_by == CREATED_BY_UNKNOWN
r.created_by = CREATED_BY_ADMIN
return r
CREATED_BY_UNKNOWN and CREATED_BY_ADMIN are values defined elsewhere in my code.

Is it possible to name (or tag) FormStack fields with simple identifiers?

This question assumes familiarity with FormStack, a drag-and-drop WYSIWYG online form builder.
Background
A client is using FormStack to manage forms. Currently, form submissions are emailed, then manually entered into a database. Predictably, my task is to automate this process. This is easy enough using FormStack's WebHooks API: I can have form submissions sent to a URL, e.g. a PHP script, and happily parse away.
Question
Is it possible to name (or tag) FormStack fields with simple identifiers?
The client needs to be able to customize the form such that multiple fields may feed into the same database column.* FormStack however, as far as I can tell, provides only a way to specify a field label, e.g. Which of these trips interest you?, not a programmer-friendly identifier, e.g. Trip. My script would have to string-compare labels (which, due to their length, are more prone to typos) to determine what to do. What are some sensible workarounds to this problem?
Clarifications*
The reason there can exist multiple fields that feed into the same database column, is that the client uses conditional fields. For example, one field might ask, Where are you studying abroad? If the user selects "Europe", a conditional field might appear, asking Which of these trips interest you?, with choices pertaining to Europe. If the user selects "Africa" however, a similar field might appear, e.g. Which of these trips interest you?, but with choices pertaining to Africa. In FormStack, these are actually two distinct fields. However, as you can imagine, the values belong in the same database column, Trip.
I have settled on a hack for now. FormStack allows HTML markup in labels, e.g. Which of these trips interest you? <!--Trip-->. The client is willing to "tag" fields in this way.
Here's a snippet of the code that parses such tags, in case it might help someone else:
require_once 'Formstack.php';
$formstack = new Formstack($apiKey);
$form = $formstack->form($_POST['FormID']);
$taggedFields = array();
foreach ($form['fields'] as $field)
{
if (preg_match('/<!--\s*([0-9A-Za-z]+)\s*-->/',
$field['label'],
$matches))
{
$taggedFields[$matches[1]] = $_POST[$field['id']];
}
}
In fact, I've had to make it a little bit more sophisticated. Some FormStack field-types serialize input (in a horrific way). For example, FormStack's Name field-type takes multiple fields (prefix, first, middle, last, initial, suffix), and concatenates the results into a string:
'first = Andrew
initial = W
last = Cheong'
To handle this, I've written my code to handle such syntax in labels as Tell us your name! <!--FirstName=first--> <!--LastName=last--> <!--MiddleInitial=initial-->
The code follows.
require_once 'Formstack.php';
$formstack = new Formstack($apiKey);
$form = $formstack->form($_POST['FormID']);
$taggedFields = array();
foreach ($form['fields'] as $field)
{
if (preg_match_all('/<!--\s*([0-9A-Za-z]+)\s*(?:=\s*(\w+))?-->/',
$field['label'],
$matches,
PREG_SET_ORDER))
{
foreach ($matches as $captures)
{
if (count($captures) == 3 &&
preg_match('/(?:^|\n|\r)'.$captures[2].' = ([^\n\r]+)/',
$_POST[$field['id']],
$subcaptures))
{
$taggedFields[$captures[1]] = $subcaptures[1];
}
else
{
$taggedFields[$captures[1]] = $_POST[$field['id']];
}
}
}
}
Hopefully, FormStack will soon add a native way to name or tag fields!

asp.net-mvc - how do i create a view to show all unapproved users and allow them to be approved

i have this code in my membership service class (taken from the asp.net-mvc sample app)
public MembershipUserCollection GetUnapprovedUsers()
{
MembershipUserCollection users = Membership.GetAllUsers();
MembershipUserCollection unapprovedUsers = new MembershipUserCollection();
foreach (MembershipUser u in users)
{
if (!u.IsApproved)
{
unapprovedUsers.Add(u);
}
}
return unapprovedUsers;
}
i now need a view to show this list of information and allow someone to approve them which will go back to the controller and set the IsApproved property to true.
Create a view which will generate a form containing label and checkbox for each member of the collection. You need to be able to get from the id of the checkbox to the user.
In the HTTP.POST Action method, iterate through the submitted fields looking for set checkboxes, when you find one set the corresponding user to approved.
Obviously the form can display arbitrary details for each user.
To use the inbuilt control helpers takes a bit more effort because you don't have a fixed size model to work with. To achieve something similar I:
Used a non-strongly typed view
populated ViewData["ids"] with IEnumerable<IdType> (which the view would loop over)
For each entry populated ViewData["field" + id] for each field I was displaying in the entity
In the view looped over the ids using ViewData["ids"] to call the HTML helpers with the id of the field.
(That was V1, in V2 I used model state so I could use the inbuilt validation error display support, but that doesn't really apply if you just want to select users.)
The POST processing was similar, repopulating the id list from the database and the looking up in the passed FormCollection.

Resources