I think it may be a bug in modelformset_factory in Django 1.2, but I'd like anyone else to double check that (possibly in newer djnago versions as well).
Just for courious people - models are representing possible scores, and sets of those available for worker's training reports.
models.py:
class ScoreSet(Model):
unit = ForeignKey(Unit)
description = CharField(max_length=20, verbose_name='description')
class Score(Model):
scoreset = ForeignKey(ScoreSet)
score = CharField(max_length=8, verbose_name='score')
description = CharField(max_length=30, verbose_name='description')
and now:
FormSet = modelformset_factory(Score, exclude='scoreset', extra=5, can_delete=True)
will produce formset with no scoreset and no score field. If you change "scoreset" field name to anything else (eg. "ss") it works fine, and excludes only "ss" field.
Exclude should be a tuple, not a string:
exclude=('scoreset',)
Related
I'm having some troubles to create a form in Django, I don't know if my aproaches are correct soy any help of where I have to read will be appreciated.
I want to show a field compose by two fields, one for hours and the other for minutes, reading Django documentation I came out with the idea to use MultiValueField, and I tried this:
class CreateEventForm(forms.ModelForm):
class TimeField(forms.MultiValueField):
def __init__(self, **kwargs):
error_messages = {
'incomplete': 'Missing info',
}
fields = (
forms.CharField(max_length=64,
label= "Horas"),
forms.CharField(max_length=64,
label="Minutos")
)
super().__init__(
error_messages=error_messages,
require_all_fields=False,
fields=fields
)
journal_start = TimeField()
journal_finish = TimeField()
class Meta:
model = UserEvent
fields = ['duration', 'pre_time', 'post_time']
labels = {
'duration': 'Duración',
'pre_time': 'Previo',
'post_time': 'Después: ',
}
But instead of having two different fields for starting and finishing journey I only have one, this is the picture of the output
I've see other questions related with MultiValueFields likes this: Django: MultiValueField and MultiWidget and these other example: https://gist.github.com/elena/3915748
But the first talks abou other subject and the last one seems to use some libraries that I don't want to use, at least for now.
How can I make a Django form field with two inputs using Model Forms?
thanks for reading me!
I have database tables with a 'TYPE' column and many other fields. In many cases, certain column values are null based on the value of 'TYPE'.
E.g.
if I have a product table , with TYPE having either 'car' or 'helicopter'. The columns are:
vertical speed, horizontal speed, and horn amplitude.
In the case of 'car' types, vertical speed should always be null , and in the case of 'helicopter' , horn amplitude should always be null.
In flask admin, is there any way to hide the fields from being submitted based on the currently selected TYPE's value?
It is fine if it is a UI level change (i.e. no backend validation is required for security/consistency purposes).
In my real life scenario, there are over 10 columns with 5+ being null in cases, so it would be very helpful if those fields can be removed in the UI (since it makes the form very long and prone to errors).
I am using flask sqlalchemy as the backend for my flask admin.
Fun question. Here is a working solution. So basically you have product types, and each type has certain valid attributes (e.g. car and honking loudness). You can also have general attributes irregardless of the type, e.g. the name of each product.
On the form_prefill, you check what fields are valid for the product type. Then you throw away the invalid fields from the form, and return the form again. It's actually pretty straightforward.
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import flask_admin as admin
from flask_admin.contrib import sqla
app = Flask(__name__)
app.secret_key = 'arstt'
db = SQLAlchemy(app)
class Product(db.Model):
id = db.Column(db.Integer, primary_key=True)
type = db.Column(db.String)
name = db.Column(db.String)
vertical_speed = db.Column(db.Integer)
rotor_rpm = db.Column(db.Integer)
honking_loudness = db.Column(db.Integer)
def __str__(self):
return "{}".format(self.name)
class ProductView(sqla.ModelView):
general_product_attributes = ['name']
product_attributes_per_product_type_dict = {
'driving': ['honking_loudness'],
'flying': ['rotor_rpm', 'vertical_speed']
}
def on_form_prefill(self, form, id):
product = self.get_one(id)
form_attributes = self.general_product_attributes + self.product_attributes_per_product_type_dict[product.type]
for field in list(form):
if field.name not in form_attributes:
delattr(form, field.name)
return form
db.create_all()
admin = admin.Admin(app, name='Example: SQLAlchemy', template_mode='bootstrap3')
admin.add_view(ProductView(Product, db.session))
helicopter = Product(type='flying', name='helicopter1', vertical_speed=99)
car = Product(type='driving', name='car2', honking_loudness=33)
db.session.add(helicopter)
db.session.add(car)
db.session.commit()
Note that this only works for the edit form, all attribtutes are still being displayed on the create form because it is not certain yet what type an product will be.
You can override create_form for create form and on_prefill_form for edit form.
in this functions you can pass some parameters to fields using form_widget_args
def create_form(self):
form = super(JobsView, self).create_form()
# kw = decide which fields to show or hide
# kw["vertical_speed"]["class"] = "hide"
self.form_widget_args = kw
return form
def on_form_prefill(self, form, id):
# kw = decide which fields to show or hide
# kw["vertical_speed"]["class"] = "hide"
self.form_widget_args = kw
I have the following query:
prefetch = Prefetch('books', queryset=Book.objects.filter(is_published=True),
to_attr='published_books')
profiles = Profile.objects.prefetch_related(prefetch)
This selects all profiles and populates them with published books.
However, I want only profiles, that actually have published books (in other words len(profile.published_books) > 0).
How can I achieve it in orm?
UPDATE:
class Book(Model):
profile = ForeignKey(Profile, related_name="books", related_query_name="book")
name = CharField(max_length=250)
is_published = BooleanField(default=True)
class Meta:
unique_together = (('profile', 'name'),)
profile_ids = Profile.objects.filter(book__is_published=True).values_list("pk", flat=True).distinct()
profiles = Profile.objects.filter(pk__in=profile_ids).prefetch_related(prefetch)
this will result a subquery that locate correct profile ids and then return any profile that match one of these ids.
use of distinct function has no effect on result but I am guessing it could improve performance (not really sure)
I have a ModelForm class in which I set a couple of the fields as ChoiceField. For one of my views, I'd like to create a form from my ModelForm class that pulls from an instance of my model in the database (like so):
form = MyModel(instance=model_instance)
When I do this and then render the form in a template, I've noticed that most of the fields are pre-populated with values pulled from the model instance, which is what I want. However, this isn't the case for two ChoiceField fields. These render as drop-down select menus with no specific option selected.
What's strange is if I don't define those two fields as ChoiceField-type in my ModelForm class, they render as normal text input fields in HTML and pre-populate using the database values. But when I define them so they show up as select-option input fields in HTML, nothing is pre-selected. Can I change this so that the values from the database are pre-selected?
EDIT: As requested here is the code for my model and form:
class App(models.Model):
CODES = (
(u'a',u'annual'),
(u'm',u'monthly'),
(u'w',u'weekly')
)
code = models.CharField(max_length=1, choices=CODES)
start_time = models.TimeField(blank=True, null=True)
end_time = models.TimeField(blank=True, null=True)
class AppForm(ModelForm):
CODES = (
(u'',u'Please select code'),
(u'a',u'annual'),
(u'm',u'monthly'),
(u'w',u'weekly')
)
TIMES = (
(u'00:00',u'All Day'),
(u'12:00',u'Noon')
)
start_time = forms.ChoiceField(required=False, choices=TIMES)
end_time = forms.ChoiceField(required=False, choices=TIMES)
code = forms.ChoiceField(choices=CODES, label='Type')
class Meta:
model = App
Interestingly, code field has the model instance value preselected just fine when rendered as HTML. I wonder if having the choices argument in the model definition makes the difference here?
UPDATE: I just noticed that if I pull up an App instance in the python manage.py shell like so:
a = App.objects.get(id=16)
a.start_time
I get a value like datetime.time(12, 0). But in the Django admin, when I'm looking at all of the App instances, all of them show (None) under start_time and end_time. Why would that be?
In response to your update : your times strings match default time string HH:MM format. Just like a user would enter them from website manually 12:00. The values get parsed and turned into time at model save (at validating really).
And when you load model - then of course the initial values loaded from object match the field's (models.TimeField) type.
If you replace your TIMES with
(datetime.time(0,0),u'All Day'),
(datetime.time(12,0),u'Noon')
your troubles should be over.
Alan
Say I have django model that looks something like this:
class Order(models.Model):
number = models...
date = models...
class OrderLine(models.Model):
# One or more lines per order
order = models.ForeginKey(Order)
common_line = models.OneToOneField(CommonLine)
class CommonLine(models.Model):
# common elements of what might be on a line item...
taxes = model...
amount = model...
I want to create a form that uses an inlineformset to edit one or more Lines (both OrderLine and CommonLine) per order.
I can create a formset that works with Order and OrderLine - but how do I get the inline formset to give me all the detailed items from the CommonLine class when displaying the formset. It seems the documentation on inline formsets requires that the inline form - the multiple lines on an order can only map to a single class...
Am I not seeing something in the documentation? I'm sure I can probably override something, I'm just not sure where.
Thanks for any help...
I solved problem with http://yergler.net/blog/2009/09/27/nested-formsets-with-django/. Pleas use the following correction in forms.py file:
instance=None
pk_value = hash(form.prefix)
+ correct_data = None
+ if (self.data):
+ correct_data = self.data;
# store the formset in the .nested property
form.nested = [
- TenantFormset(data=self.data,
+ TenantFormset(data=correct_data,
instance = instance,
Just working on Django 1.4.1 very well.
Some minor changes were needed to make Nathan's code at http://yergler.net/blog/2009/09/27/nested-formsets-with-django/ work in Django 1.3. The line below causes a ManagementForm Error.
TenantFormset = inlineformset_factory(models.Building, models.Tenant, extra=1)
Usings the modelformset_factory and manually defining the queryset seems to work, but I have not implemented the ability to add extras.
TenantFormset = modelformset_factory(models.Tenant, extra=0)
form.nested = [
TenantFormset(
queryset = Tenant.objects.filter(building = pk_value),
prefix = 'value_%s' % pk_value
)
]
I also had to manually pass data to the sub-sub-forms in the is_valid method:
def is_valid(self):
result = super(BaseProtocolEventFormSet, self).is_valid()
for form in self.forms:
if hasattr(form, 'nested'):
for n in form.nested:
n.data = form.data
if form.is_bound:
n.is_bound = True
for nform in n:
nform.data = form.data
if form.is_bound:
nform.is_bound = True
# make sure each nested formset is valid as well
result = result and n.is_valid()
return result
EDIT:
New instances can be created using jQuery. See this question:
This sounds very similar to the approach talked about at help http://yergler.net/blog/2009/09/27/nested-formsets-with-django/ where Nathan writes about how he catered for "a multi-level data model; an example of this kind of model would be modeling City Blocks, where each Block has one or more Buildings, and each Building has one or more Tenants."
Some more explanations can aslo be found here Django Forms Newbie Question