I want to change formfield widget depend on other field value. Default is select because model field is foreign key. My models are as follows:
class ProductFeatureValue(BaseName):
feature = models.ForeignKey('ProductTypeFeature')
class Meta:
verbose_name = _(u'Product Feature Value')
verbose_name_plural = _(u'Product Feature Values')
class ProductFeature(models.Model):
product = models.ForeignKey('Product')
feature = models.ForeignKey('ProductTypeFeature')
value = models.ForeignKey('ProductFeatureValue')
And my form is as follows:
class ProductFeatureFormForInline(ModelForm):
class Meta:
model = ProductFeature
def __init__(self,*args,**kwargs):
super(ProductFeatureFormForInline,self).__init__(*args,**kwargs)
if isinstance(self.instance,ProductFeature):
try:
widget_type = self.instance.feature.product_type.producttypefeature_set.all()[0].widget #TODO Fix that 0 slice
if widget_type == u'TEXT':
self.fields['value'] = CharField(widget=TextInput())
if widget_type == u'MULTIPLE_SELECT':
self.fields['value'].widget = MultipleChoiceField()
except:
pass
It changes the fields widget but when it make it charfield and populate it with instance it shows the id of the model not the value (author : 1) and it makes sense to show it that way, but i want to show(author: Dan Brown).
I have tried with initial values but not working. Any tips of doing that will be highly appreciated. Thanks
Your __unicode__() method on the model should dictate what is shown there, if I'm not missing something.
On your model:
class ProductFeatureValue(BaseName):
[... snipped code ...]
def __unicode__():
return self.feature
This snippet assumes that self.feature is what you want to return, and not something else on the parent BaseName.
Related
I'm trying to show one of three different child model inlines for a parent model, based on a selected field value within the parent model. I have tried every solution that pops up from various google searches but nothing seems to work.
First a little background, I'm new to python and django but so far I feel that I have been a quick study. I'm attempting to build a web application to house information linked to various spatial locations. The geometry type (geom_type) for each location may be different (i.e., points, linestring, and polygons are possible). To capture this information I plan to create a parent model (Location) to house the name and geom_type (and possibly other metadata). The spatial data related to each Location would then be housed in three separate child models; one for each geom_type. When entering data I would like to create a new location and select the geom_type, which would then pull up the correct inline.
Now for the details:
Models
from django.contrib.gis.db import models
class Geometry(models.Model):
TYPE = (
('Point', 'Point'),
('Linestring', 'Linestring'),
('Polygon', 'Polygon'),
)
geom_type = models.CharField('Geometry Type', choices = TYPE, max_length = 30)
class Meta:
verbose_name = 'Geometry'
verbose_name_plural = 'Geometries'
def __str__(self):
return self.geom_type
class Location(models.Model):
name = models.CharField('Location Name', max_length = 50)
geom_type = models.ForeignKey(Geometry, on_delete=models.CASCADE)
def __str__(self):
return self.name
class Point(models.Model):
name = models.OneToOneField(Location, on_delete=models.CASCADE)
geometry = models.PointField()
def __str__(self):
return self.name.name
class Linestring(models.Model):
name = models.OneToOneField(Location, on_delete=models.CASCADE)
geometry = models.LineStringField()
def __str__(self):
return self.name.name
class Polygon(models.Model):
name = models.OneToOneField(Location, on_delete=models.CASCADE)
geometry = models.PolygonField()
def __str__(self):
return self.name.name
Admin
from django.contrib.gis import admin
from leaflet.admin import LeafletGeoAdmin, LeafletGeoAdminMixin
from .models import Geometry, Location, Point, Linestring, Polygon
class GeometryAdmin(admin.ModelAdmin):
list_display = ('id', 'geom_type')
admin.site.register(Geometry, GeometryAdmin)
class PointInline(LeafletGeoAdminMixin, admin.StackedInline):
model = Point
class LinestringInline(LeafletGeoAdminMixin, admin.StackedInline):
model = Linestring
class PolygonInline(LeafletGeoAdminMixin, admin.StackedInline):
model = Polygon
class LocationAdmin(admin.ModelAdmin):
model = Location
list_display = ('id', 'name', 'geom_type')
inlines = [
PointInline,
LinestringInline,
PolygonInline
]
admin.site.register(Location, LocationAdmin)
All three inlines show up correctly with the code above as expected. However, when I try to incorporate the conditional logic with different variations of get_inlines or get_inline_instances it always just ends up displaying the inline associated with the final "else" statement.
My failed attempt
def get_inlines(self, request, obj: Location):
if obj.geom_type == 'Point':
return [PointInline]
elif obj.geom_type == 'Location':
return [LinestringInline]
elif obj.geom_type == 'Polygon':
return [PolygonInline]
else:
return []
I believe the problem occurs because conditional statements are not referencing the model field correctly. But I can't seem to stumble upon the correct way to achieve my expected outcome.
Use related_name in model like below:
next_question = models.ForeignKey(Question, on_delete=models.CASCADE, null = True, blank = True, related_name='next_question', limit_choices_to={'is_active': True})
and then fk_name Like the example below: Then try. Hope you can find a solution by yourself.
class Labels(admin.TabularInline):
model = Label
extra = 0
fk_name = "next_question"
Use admin.StackedInline for OneToOne and admin.TabularInline for ForeignKey.
class ProfileInline(admin.StackedInline):
model = Profile
can_delete = False
Create separate admin for 'Geometry' and 'Location' if you stacked.
I implemented a dependent dropdown on my Django webapp. I used this tutorial Implementing dependent drop down. However, challenge comes when I want to update the form. To put this in perspective, let me recreate the code here.
Model.py
class VehicleMake(models.Model):
make = models.CharField(max_length=20)
manufacturer = models.CharField(max_length=20)
def __str__(self):
return self.make
class VehicleModel(models.Model):
make = models.ForeignKey(VehicleMake, on_delete=models.CASCADE)
model_name = models.CharField(max_length=20)
def __str__(self):
return self.model_name
class Vehicle(models.Model):
model = models.ForeignKey(VehicleModel, on_delete=models.CASCADE)
description = models.TextField()
Notice that unlike in the provided tutorial, I don't have both of the dependent fields on the vehicle model. That is to avoid repetition since if you know the vehicle model, you will definitely know the make from the VehicleModel table.
Here is the form:
forms.py
class VehicleDetails(forms.ModelForm):
make = forms.ModelChoiceField(queryset=VehicleMake.objects.all(),
empty_label="Select Vehicle Make")
class Meta:
model = Vehicle
fields = ['make', 'model', 'description'
]
def __init__(self, *args, **kwargs):
super(VehicleDetails, self).__init__(*args, **kwargs)
self.fields['model'].queryset = VehicleModel.objects.none()
if 'make' in self.data:
try:
make = int(self.data.get('make'))
self.fields['model'].queryset = VehicleModel.objects.filter(make=make).order_by('model_name')
except (ValueError, TypeError):
pass # invalid input from the client; ignore and fallback to empty VehicleModel queryset
elif self.instance.pk:
vehicle_model = VehicleModel.objects.get(self.instance.model)
self.fields['make'] = vehicle_model.make
self.fields['model'].queryset = self.instance.model_set.filter(make=vehicle_model.make).order_by('model_name')
So, my challenge is, when I want to update the form, I get an error from the last section of the form under the elif code. I want to get the value of make using the store value of model then use that to render the form of course with the select of options of model being those from the selected make, unless the user now makes changes to the make field.
This is what I have tried so far (especially under the elif section on the forms.py) but I keep getting the error: TypeError: 'VehicleModel' object is not iterable. What am I doing wrong?
I was able to solve this by changing this in the elif block:
vehicle_model = VehicleModel.objects.get(pk=self.instance.model.id)
self.fields['make'].queryset = self.instance.model.make.__class__.objects.all()
self.fields['model'].queryset = self.instance.model.__class__.objects.filter(make=vehicle_model.make).order_by('model_name')
Then all I had to do at the views.py to ensure that current value is loaded was to add a initial value while loading the form, i.e.
vehicle_form = VehicleDetails(instance=listing.vehicle, initial = {'make': listing.vehicle.model.make })
I hope it helps anyone in the same problem.
I have two models with their respective forms. One has a Foreign Key link to the other and from, here I would like to set some fields default data.
class Lexicon(models.Model):
[...]
case_sensitive = models.BooleanField(default=True)
invariant = models.NullBooleanField(default=False)
diacritics = models.BooleanField(default=True)
[...]
class Meta:
verbose_name = "lexicon"
ordering = ["filename"]
def __str__(self):
return self.filename
class Lexeme(models.Model):
lexicon = models.ForeignKey(Lexicon, on_delete=models.CASCADE)
case_sensitive = models.BooleanField(default=True)
diacritics = models.BooleanField(default=True)
[...]
class Meta:
verbose_name = "lexeme"
I would like the Lexeme model fields "case_sensitive" and "diacritics" to default from Lexicon. I suppose the forms may be a better place to do this.
Any idea ?
As I understand, you only need to populate data from Lexicon to Lexeme model fields. You can override get_form_kwargs in your FormView as follows
def get_form_kwargs(self):
lex_obj = Lexeme.objects.get(pk=self.kwargs['pk'])
kwargs = super().get_form_kwargs()
kwargs['initial']['case_sensitive'] = lex_obj.lexicon.case_sensitive
kwargs['initial']['diacritics'] = lex_obj.lexicon.diacritics
return kwargs
Is that what you want? I have not tested but, I have used similar thing on my project. Let me know if works or not.
I finally found the way to go. It was just basic initial setting of field, no need to touch to forms.py, models.py nor the html template.
I passed data to my form like this:
lexeme_form = LexemeForm(initial={'case_sensitive': lexicon.case_sensitive, 'diacritics': lexicon.diacritics})
use Ajax at template to change the initial value of "case_sensitive" and "diacritics" when Lexicon changed, and abstract model can be used to reduce repeat lines :
class BaseLex(models.Model):
case_sensitive = models.BooleanField(default=True)
diacritics = models.BooleanField(default=True)
class Meta:
abstract = True
class Lexicon(BaseLex):
# without `case_sensitive` and `diacritics' fields
...
class Lexeme(BaseLex):
# without `case_sensitive` and `diacritics' fields
lexicon = models.ForeignKey(Lexicon, on_delete=models.CASCADE)
...
I am creating a reference lexicon in django. The lexicon is a translator for certain fields from one language to another.
The lexicon goes like this: 'id','lng1','lng2','lng3'
I have a model that uses the lexicon in one of the fields.
class lexicon(models.Model):
lng1 = model.Charfield(max_length=255)
lng2 = model.Charfield(max_length=255)
lng3 = model.Charfield(max_length=255)
lexicon_choices = (
(lng1=lng1),
(lng2=lng2),
(lng3=lng3)
)
class model(models:Model):
model_name = model.Charfield(max_length=255, unique)
field1 = model.Charfield(max_length=3,CHOICES=lexicon_choices)
This works ok, but I want to know, is there a way to glean out the column names from class lexicon and port them as the choices for class model? I am using Django Rest Framework for the first time, and I want to make sure the right lng is there.
Thanks
You can use a ForeignKey on the lexicon class to your main model. This will ensure a One to Many relationship. And you can store the values in database.
yes, possible. but in admin.py
class CustomForm(forms.ModelForm):
class Meta:
model = YourModel
def __init__(self, *args, **kwargs):
super(CustomForm, self).__init__(*args, **kwargs)
choices = [(x.id, x.name) for x in Lexicon.objects.all()]
self.fields['field1'].choices = choices
class YourModelAdmin(admin.ModelAdmin):
form = CustomForm
and your original model looks like:
class YourModel(models:Model):
model_name = model.Charfield(max_length=255, unique)
field1 = models.CharField(max_length=10, choices=(('--', '--'),))
IMPORTANT: do not name your class names with lower case. ;)
I'm using admin.TabularInline in my admin code for which I've made a custom form.
class RateCardForm(forms.ModelForm):
category = forms.ModelChoiceField(queryset=models.Category.objects.all(), label='Category')
class Meta:
model = models.RateCard
fields = ('category')
class RateCardInline(admin.TabularInline):
model = models.RateCard
form = RateCardForm
extra = 3
The problem is that after I've saved my model instance, whenever I edit the model instance, it would remove the pre-selected choice and I'll have to select the choice again. Any ideas as to how to stop it?
Also for ModelChoiceField if I don't specify the label, then it would come up as None on admin page, but I don't need to specify it for admin.StackedInline.
To preselect the currently selected category instance you can set its primary key to the field's initial value by overriding __init__() on the ModelForm:
class RateCardForm(forms.ModelForm):
category = forms.ModelChoiceField(queryset=models.Category.objects.all(), label='Category')
class Meta:
model = models.RateCard
fields = ('category')
def __init__(self, *args, **kwargs):
super(RateCardForm, self).__init__(*args, **kwargs)
instance = kwargs.get('instance')
# Instance will be None for the empty extra rows.
if instance:
selected_pk = # query the primary key of the currently selected category here
self.fields['category'].initial = selected_pk