Good day SO. I am trying to use the get_FOO_display on template but my data is not displaying. Also, I have read other SO answers and I believe that I followed them to a T but not working. Please try to correct me what I do wrong.
How I declared:
Models:
GENDER_CHOICES = (
(1, '男性'),
(2, '女性'),
)
gender = models.PositiveSmallIntegerField(choices=GENDER_CHOICES, default=0, null=False)
forms:
...
self.fields['gender'].widget.attrs.update({'class': 'form-control'})
view:
account = AccountViewForm(initial = {"gender" : account.gender})
template:
{{account.get_gender_display}}
If I only use {{account.gender}}, it becomes a select tag with the correct data. I want to display this as a TEXT tag.
Note. I also cant do <input type="text" value="{{account.gender}}" id="id_gender"> manually since this messes up the template.
It's because you're passing the .gender property, you need the whole model instance that contains the .gender attribute (Account model?)
the .get_FOO_display exists on the model as whole, not the field.
pass the account object in the context and use account_obj.get_foo_display()
Related
I'm rendering a form in a django template using this model:
class Group(models.Model):
name = models.CharField(max_length=100)
description = models.TextField()
members = models.IntegerField(default=0)
has_max = models.BooleanField(default=False)
max_size = models.IntegerField(default=10)
DAYS = [
('Sundays', 'Sundays'),
('Mondays', 'Mondays'),
('Tuesdays', 'Tuesdays'),
('Wednesdays', 'Wednesdays'),
('Thursdays', 'Thursdays'),
('Fridays', 'Fridays'),
('Saturdays', 'Saturdays'),
]
meeting_day = MultiSelectField(
verbose_name = 'Meeting day(s)',
choices=DAYS,
max_choices=6,
max_length=100
)
start_time = TimeField(widget=TimePickerInput)
end_time = TimeField(widget=TimePickerInput)
def __str__(self):
return self.name
And onto this template:
<form method="POST">
{% csrf_token %}
{{form.as_p}}
<button>POST</button>
</form>
I have a couple of issues going forward:
My first issue is that I only want to have max_size be displayed in my template form if the user clicks has_max. Like a conditional, where once the user checks the box, changing has_max to True then they can enter in the max size of the group.
My second issue is that the start_time and end_time to render in my template or on the admin side. I'm not sure how TimePickerInput works, but right now, there are no fields in my template form or admin form that have those time fields.
Also, last thing, if I have both names the exact same (i.e., ('Sundays', 'Sundays'), is it necessary to have both? Or can django figure it out.
The first Problem as I understand it You want to have this check box and when it will true the Field of max_size will appear this problem needs to solve with javascript
Why ?
Because if You use the Django template you will need to refresh the page when the user changes something and this will overload the server So you need to create an event with js when the use click on the checkbox it will disappear
Also, last thing, if I have both names the exact same (i.e., ('Sundays', 'Sundays'), is it necessary to have both? Or can Django figure it out?
This part ... You need to know that the first value is the value will be stored in the database and the second value is the human-readable value this what will be displayed for the user in the form Read This
You must know you can't put widgets into your model if you want to add it add it to your from in Your admin or form files ... You will override the AdminModel in your admin file and change the formvalue Read This
My goal is to be able to select a location and Input part numbers without seeing this quote field. I dont even completely understand what this select box is looking for. I have Quote objects saved and yet these are not coming up as selectable options. Not that I want them to, Im just saying. My thinking regarding the seelctable options is that this would be auto-populated? You can probably tell my confusion even in my explanation. Ultimately, I dont want to see a select box at all as Im not really interested in whatever this pointing to, but just for kicks would like to know what it is trying to point to.
quote/Models.py
class Quote(models.Model):
QUOTE_ENVIRONMENTS = (
('testing', 'Test'),
('production', 'Production')
)
SALES_SOURCE=((1, 'Marketplace'),
(2, 'Webstore'),
(3, 'Physical Store'),
(4, 'Phone')
)
environment = models.CharField(max_length=20, choices=QUOTE_ENVIRONMENTS, default="testing")
sales_source = models.IntegerField(choices=SALES_SOURCE, null=True)
order_notes = models.TextField(blank=True)
locations = models.ManyToManyField('products.ProductSelection')
products/models.py
class Product(models.Model):
pass
class Warehouse(models.Model):
pass
class ProductSelection(models.Model):
location = models.ForeignKey('Warehouse', on_delete = models.CASCADE)
product = models.ManyToManyField('Product')
Admin.py
class ProductOrderForm(forms.ModelForm):
locations = forms.ModelChoiceField(queryset= Warehouse.objects.all())
part_number = forms.IntegerField()
def clean_product_id(self):
cd = self.cleaned_data
logger.info(cd)
value = cd['part_number']
if value not in Products.objects.list_part_numbers():
raise forms.ValidationError("Not a valid partnumber")
class ProductSelectionTabularInline(admin.TabularInline):
form = ProductOrderForm
model = Quote.locations.through
class QuoteAdmin(admin.ModelAdmin):
list_display=['id', 'environment', 'order_notes','sales_source']
list_editable = ['environment', 'sales_source', 'order_notes']
inlines = [ProductSelectionTabularInline]
exclude=['quote']
Error when using exclude attr.
ERRORS:
<class 'orders.admin.ProductSelectionTabularInline'>: (admin.E201) Cannot exclude the field 'quote', because it is the foreign key to the parent model 'orders.Quote'.
I dont want the left most box. Thanks for your help
I figure out that the field to the left is the ProductSelection instance. I confused myself by adding the other 2 form widgets. So this does not allow me to do what I want which is to edit the parts to the locations and add it to the form for creating a quote.
I have created a model manager to be able to filter data
class BOMVersion_default_active_Manager(models.Manager):
def get_queryset(self):
return super(BOMVersion_default_active, self).get_queryset().filter(is_default=True,is_active=True)
#with_author
class BOMVersion(models.Model):
version = IntegerVersionField( )
name = models.CharField(max_length=200,null=True, blank=True)
description = models.TextField(null=True, blank=True)
material = models.ForeignKey(Material)
default_active_objects = BOMVersion_default_active_Manager()
I try to use it in my nested for loop from template ( since I cant filter directly in template and this is how i decided to overcome this limitation)
{% for bomversion in soproduct.product.material.bomversion.default_active_objects_set.all %}
But I am not getting any output. What could be the problem? Can I do it in general?
Using a custom Manager is not the right way to go. You need to carefully read the whole Django Managers article.
In this situation what you need is a custom QuerySet. Something like this:
class BOMVersionQuerySet(models.QuerySet):
def active(self):
return self.filter(is_active=True)
def default(self):
return self.filter(is_default=True)
#with_author
class BOMVersion(models.Model):
version = IntegerVersionField( )
name = models.CharField(max_length=200,null=True, blank=True)
description = models.TextField(null=True, blank=True)
material = models.ForeignKey(Material)
objects = BOMVersionQuerySet.as_manager()
Now you can use .active() and .default() methods to filter any BOMVersion-QuerySet. This is actually what you got when you use the reverse relation from Material model, material.bomversion_set is a BOMVersion-QuerySet and thus you don't have access to BOMVersion.objects, but since its a BOMVersion-QuerySet you can use .active() and .default()
{% for bomversion in soproduct.product.material.bomversion_set.default.active %}
However, again this is completely wrong and my advice is not to do it in the template. Use the view to build you querysets, and only iterate them in the template.
Why its bad in the template? Because right now you are making 1 query per material object, and there is no way to optimize it unless you use prefetch_related, but guess what? You can't use prefetch_related in the django template system. (Its on purpose, and the reason is to not do stuff like that in the template), so the correct approach is to make something similar to this:
#in the view:
soproduct = SoProduct.objects.select_related('product__material').prefetch_related(
Prefetch(
'product__material__bomversion_set',
queryset=BOMVersion.objects.default().active()
to_attr='default_active_bomversions'
)
).get(pk=soproduct_id)
#in the template:
{% for bomversion in soproduct.product.material.default_active_bomversions %}
Now you are gonna make 1 query for the soproduct with its related product and material data and 1 more query for the requested default_active_bomversions.
I have the following models,
class TblMaterials(models.Model):
name = ...
....
class TblCategoris(models.Model):
name = ...
....
class TblMaterialCategories(models.Model):
tbl_categories = models.ForeignKey(TblCategories, blank=True, null=True)
tbl_materials = models.ForeignKey('TblMaterials', blank=True, null=True)
and in my home page i want to print all the materials and related material category within. Obviously there could be some materials without any categories.
in my view i try something like:
TblMaterials.objects.all().select_related('tblmaterialcategories_set')
and in templates:
{%for mat in materials%}
{{mat.name }}
{%for cat in mat.tblmaterialcategories_set.all %}
{{cat.tbl_categories.name}} ,
{%endfor%}
{%endfor%}
I dont think select_related works in that _set item.
I want to achieve something like that without making queries for each item. If I can add another field to material query set and access it like mat.categories and for loop in it, it is also appriciated.
What could be the best way to display all materials and their categories?
Thanks.
Select related can be used for the foreign keys which are in the current model. For an example:
class TblMaterials(models.Model):
name = ...
....
Foreign Key: TblMaterialCategories
While calling the TblMaterial model , if you want to use select related with TblMaterialCategories
TblMaterials.objects.all().select_related('tblmaterialcategories')
It will work.
I want to show the human-readable name for the type selected but I
keep getting the stored value.
TYPE_CHOICES = (
('0', 'Basic'),
('1', 'Full'),
('2', 'Intermediate'),
)
class ServiceType(models.Model):
type = models.IntegerField(max_length=1, choices=TYPE_CHOICES)
amount = models.DecimalField(max_digits=10, decimal_places=2)
def __unicode__(self):
return '%s' % (self.get_type_display())
It seems that you have your answer, but as another link, I'd just like to point out James Bennett's thoughts on this:
Handle choices the right way
I think it is a pretty convenient way to do things, and removes the 'magic number' aspect of things. Worth a read IMO, even if you go for a different option.
From his article (quoted in case it disappears):
class Entry(models.Model):
LIVE_STATUS = 1
DRAFT_STATUS = 2
HIDDEN_STATUS = 3
STATUS_CHOICES = (
(LIVE_STATUS, 'Live'),
(DRAFT_STATUS, 'Draft'),
(HIDDEN_STATUS, 'Hidden'),
)
# ...some other fields here...
status = models.IntegerField(choices=STATUS_CHOICES, default=LIVE_STATUS)
Now we can just import the Entry model and query like so:
live_entries = Entry.objects.filter(status=Entry.LIVE_STATUS)
draft_entries = Entry.objects.filter(status=Entry.DRAFT_STATUS)
You probably want to use ChoiceField instead of IntegerField in your model. It sounds like you are seeing an input tag with type=text in your admin but want a select tag. The default widget associated with a IntegerField is TextInput which would explain what you are seeing.
Another option is to write your own admin and explicitly call out that you want type to be a ChoiceField in the admin. Something like this:
class ServiceTypeAdmin(admin.ModelAdmin):
# ...
type = fields.ChoiceField(choices=TYPE_CHOICES)
admin.site.register(ServiceType, ServiceTypeAdmin)
I would personally start by switching the IntegerField to a ChoiceField. Way less work involved.
I had that same problem, and couldn't figure out why it works, but if you change the field type to CharField the get_type_display should work fine.
TYPE_CHOICES = (
('B', 'Basic'),
('F', 'Full'),
('I', 'Intermediate'),
)
class ServiceType(models.Model):
type = models.CharField(max_length=1, choices=TYPE_CHOICES)
amount = models.DecimalField(max_digits=10, decimal_places=2)
Rookie mistake, I've changed the tuple values from ('0', 'Basic) to (0, 'Basic') and it worked. I didn't realize that I was saving a char value as an integer value.
Thanks for your help.
Trick
use TypedChoiceField()
The answer of your questoin lies in using TypedChoiceField, not ChoiceField.
you are getting type field from a django form, using the cleaned_data from a ChoiceField. The problem with this is that the output from a ChoiceField is a string, not an integer.
if you use get_type_display() right after saving the form, u would probably get the value, but when u have try to retrieve the value from the DB, you would get integer instead of string(because your are saving type as Integer field), here you wont be able to get value with get_type_display.
Having now looked into this, I see that you should have used the TypedChoiceField, to ensure that the output from cleaned_data is always an integer or string.
first of all change IntergerField to Char field or SmallIntergetField.
Hope this helps.
Code
type = models.SmallIntegerField(choices=TYPE_CHOICES)
in forms.py
type = TypedChoiceField(coerce=int, required=False, empty_value=0, choices=TYPE_CHOICES)
another possibility is that you could use MODELFORM and provide the widgets for the field.
Forms.py
class abc(forms.Modelform)
class Meta:
model = FOO
widgets = {
'type': forms.TypedChoiceField(coerce=int, required=False, empty_value=0, choices=TYPE_CHOICES),
type shadows with an in-built bulletin, that's why get_type_display() has no effects. Avoid using column names as type instead use service_type or something else and use get_service_type_display() i.e get_<column_name>_display().