I am using Django 1.97 and have the following models:
class Symbol(models.Model):
symbol = models.CharField(max_length=15)
# more fields
class Position(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
symbol = models.ForeignKey(Symbol)
# more fields
def get_user_positions_qs(self, user):
positions = Position.objects.all().select_related('symbol').filter(user=user).order_by('symbol')
return positions
I need to display a modelform in a template for the logged in user's positions, but the symbol field needs to be disabled. So far I have the following in my view:
position = Position()
form_class = PortfolioForm
PositionModelFormSet = modelformset_factory(Position, fields=('symbol', 'various_other_fields'), form=form_class)
def get(self, request):
positions = self.position.get_user_positions_qs(user=request.user)
position_formset = self.PositionModelFormSet(queryset=positions)
return render(request,
'template.html',
{'position_formset': position_formset})
And the form:
class PortfolioForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(PortfolioForm, self).__init__(*args, **kwargs)
self.fields['symbol'].widget = forms.TextInput(attrs={'disabled': True})
class Meta:
model = Position
fields = ['symbol', 'various other fields']
The problem is that when displaying the form, the symbol field only contains the foreign key id instead of the actual symbol CharField from the symbol model. If I change the form so that the symbol field is not disabled, then the symbol field in the template displays the correct value, however it then has a dropdown allowing the user to change the symbol which is not allowed.
So my question is, how do I disable the symbol field in the template while continuing to display the symbol value found in the CharField from the symbol model (ie: not just the foreign key id pointing to that record). It appears that if you have a form that includes a field from another model, then trying to make that field disabled results in the field only displaying a foreign key instead of the actual value from the other table.
The problem is that you are using a Textinput widget for a FKey field, which is treated as a lookup field by default. Why not just have it be a disabled dropdown field?
self.fields['symbol'].widget.attrs['disabled'] = 'disabled'
Edit: Alternative solution
Based on your comment, here's another solution that I think should work:
class PortfolioForm(forms.ModelForm):
symbol_text = forms.CharField()
class Meta:
fields = ('symbol', 'other fields')
model = Position
def __init__(self, *args, **kwargs):
super(PortfolioForm, self).__init__(*args, **kwargs)
self.fields['symbol_text'].widget.attrs['value'] = self.instance.symbol
self.fields['symbol_text'].widget.attrs['disabled'] = 'disabled'
Related
When I render the form to a HTML template,
a certain field of form which is initiated by init is ordered always at bottom of table, even though I defined the field at the middle of form class.
Is there any way or method to customize the order of fields in the form where a initiated field exists by init.
I wanna put the field in the middle of form table in HTML template.
A screenshot of the rendered template:
In the screenshot,
the field "category_name" is ordered at the bottom of tag
I wanna change the order to the middle of table.
I am using Django 2.2 and python 3.7 on Windows 10.
Thanks
from django import forms
from .models import Category_name
class MakePurchaseLog(forms.Form):
buy_date = forms.DateField(input_formats=['%Y-%m-%d'])
shop = forms.CharField(max_length=50)
def __init__(self, user, *args, **kwargs):
super(MakePurchaseLog, self).__init__(*args, **kwargs)
self.fields['category_name'] = forms.ChoiceField(
choices = [(item.category_name, item.category_name) \
for item in Category_name.objects. \
filter(owner=user)])
goods_name = forms.CharField(max_length=50)
price = forms.IntegerField(min_value=0)
memo = forms.CharField(max_length=50, required=False)
field_order = ['category_name']
The fact that the __init__ is placed in the middle of the class, will not make any difference, since this is a function, and the evaluation is thus "postponed" until you actually create a MakePurchaseLog form object.
I think the most elegant solution here is to just already define the field in your form, and then in the __init__ function alter the choices, like:
class MakePurchaseLog(forms.Form):
buy_date = forms.DateField(input_formats=['%Y-%m-%d'])
shop = forms.CharField(max_length=50)
category_name = forms.ChoiceField()
goods_name = forms.CharField(max_length=50)
price = forms.IntegerField(min_value=0)
memo = forms.CharField(max_length=50, required=False)
def __init__(self, user, *args, **kwargs):
super(MakePurchaseLog, self).__init__(*args, **kwargs)
self.fields['category_name'].choices = [
(item.category_name, item.category_name)
for item in Category_name.objects.filter(owner=user)
]
We thus populate the choices of the category_name field in the __init__ function, but we define the field already at the class level.
I have two models:
class Entity(models.Model):
entity = models.CharField(primary_key=True, max_length=12)
entityDescription = models.CharField(max_length=200)
def __str__(self):
return self.entityDescription
class Action(models.Model):
entity = models.ForeignKey(Entity, on_delete=models.CASCADE, db_column='entity')
entityDescription = models.CharField(max_length=200)
action = models.CharField(max_length=50)
def __str__(self):
return '%s' % self.entity
I have a model form and model formset, along with a form helper for crispy-forms:
class ActionForm(ModelForm):
class Meta:
model = Action
fields = '__all__'
def __init__(self, *args, **kwargs):
super(AlertForm, self).__init__(*args, **kwargs)
instance = getattr(self, 'instance', None)
if instance and instance.pk:
disabledFields = ['entity',
'entityDescription']
for field in disabledFields:
self.fields[field].disabled=True
else:
self.fields['entity'].blank=True
self.fields['entityDescription'] = ModelChoiceField(queryset=Entity.objects.all())
ActionFormSet = modelformset_factory(Action, extra=1, exclude=(), form=ActionForm)
class ActionFormsetHelper(FormHelper):
def __init__(self, *args, **kwargs):
super(ActionFormsetHelper, self).__init__(*args, **kwargs)
self.form_method = 'post'
self.template = 'bootstrap/table_inline_formset.html'
self.add_input(Submit("submit", "Submit"))
self.layout = Layout(
Field('entity', css_class="input", type="hidden"),
Field('entityDescription', css_class="input"),
Field('action', css_class="input")
)
I have a view:
def actions(request):
newActions = Action.objects.filter(action='')
formset = ActionFormSet(request.POST or None, queryset=newActions)
helper = ActionFormsetHelper()
context = {'formset':formset, 'helper':helper}
if request.method == 'POST':
for form in formset:
if form.is_valid():
if form.has_changed():
obj = form.save(commit=False)
obj.entity = form.cleaned_data['entityDescription']
obj.save()
return HttpResponseRedirect('/actions')
return render(request, 'actions/actions.html', context)
So my rendered page looks something like this:
entityDescription action
Jim Halpert [Blank Cell]
Michael Scott [Blank Cell]
[Blank Cell] [Blank Cell]
entity is hidden, and entityDescription is driven by the Entity model. When the user selects an entityDescription, I would like entity to be autopopulated in the Action model. Logically, this means entityDescription would need to go back to the Entity model, find the corresponding entity primary key, and place that value in the entity foreign key in the Action model.
My attempt at this is in the view. I saved the form without committing, tried to assign some value to entity, then attempted to commit the form. This attempt results in this error:
Cannot assign "<Some Entity Description>": "Action.entity" must be a "Entity" instance.
This makes sense, because I tried to just assign the entityDescription to entity instead of assigning the entity. I next tried to just get the entity in a hacky manner since it is the first word in entityDescription:
obj.entity = form.cleaned_data['entityDescription'].split(' ', 1)[0]
This resulted in the same error, despite entity looking correct in the error. These errors are occurring for both the existing model formset members AND the new member.
How do I retrieve the primary key of the Entity model when the user selects a value from the Entity model? Then how do I assign that primary key to the corresponding foreign key field in the Action model?
Edit:
So Jim and Michael are existing records in Action. The user can assign them an action. The blank line is a new action. The user can choose the entityDescription from the Entity model. entity is a hidden field (i.e. 1 for Jim, 2 for Michael).
When the user selects an entityDescription for the new line (i.e. user selects Jim), the primary key (1) should be entered into the hidden entity field prior to saving the forms.
Another Edit:
After further investigation, if I implement the solution in the provided answer, the problem is here:
obj.entity = Entity.objects.get(pk=pk)
This is actually returning the entityDescription of the Entity model (i.e. what is defined by def __str__) rather than the primary key. I attempted to change this to...
obj.entity = Entity.objects.get(pk=pk).entity
...but this results in the primary key being returned as a string rather than an object. Therefore it can't be saved to the database. How would I go about turning this string into an object? In other words, how do I use the query language to get one value from one field from one object of a Django model?
Your obj.entity should assign as object, and form.cleaned_data isn't return as object.
try to print what the output of this, If I following your case, makesure it as id/pk from the selected entity:
print(form.cleaned_data['entityDescription'])
# eg: 16
Then:
obj.entity = form.cleaned_data['entityDescription']
should be;
pk = form.cleaned_data['entityDescription']
obj.entity = get_object_or_404(Entity, pk=pk)
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this is object instance
# OR
pk = form.cleaned_data['entityDescription']
obj.entity = Entity.objects.get(pk=pk)
# ^^^^^^^^^^^^^^^^^^^^^^^^^ this is object instance
I keep running into this error:
Cannot assign "u'Foo Group'": "Team.membership_group" must be a "Group" instance.
In my Django application, I give the user an option to create a Team. The team has a memebership_group ForeignKey attribute which maps to 'Group' (django.contrib.auth.models.Group). In the form, I've changed the widget to be a CharField so that if a group that the user types isn't actually a group, my code should create it. Here is my form:
class TeamForm(ModelForm):
"""Form to create and modify systems"""
membership_group = CharField()
manager = ModelChoiceField(queryset=Manager.objects.all(), required=True)
class Meta:
model = Team
fields = ['name', 'manager', 'membership_group']
In my views (or possibly I need to write the code elsewhere?), I want to take the string value and run a get_or_create to either return the existing group or create a new one. Here is the code in my views that isn't working:
class TeamCreateView(AutoEventLogMixin, SuccessMessageMixin, PermissionRequiredMixin, CreateView):
"""View to create Teams"""
form_class = TeamForm
model = Team
permission_required = 'teams.add_team'
success_message = "Team '%(name)s' created successfully."
template_name = 'teams/team_form.html'
def form_valid(self, form):
team_created = super(TeamCreateView, self).form_valid(form)
team = self.object
group_name = form.instance.membership_group
group_name.encode('utf-8')
membership_group = Group.objects.get_or_create(name=group_name)[0]
team.membership_group = membership_group
team.save()
return team_created
What am I doing wrong? What code do I need to add (and where) to be able to serialize/deserialize the membership group value from string to group and vice versa
I think in anyway it is not a good idea to do type translation in form_valid. If you use django1.9, then consider to create a customized FormField:
from django import forms
class MyGroupField(forms.Field):
def to_python(self, group_name):
return Group.objects.get_or_create(name=group_name)[0]
class TeamForm(ModelForm):
membership_group = MyGroupField()
My forms.py
class CreateVesselForm(forms.ModelForm):
class Meta:
model = Vessel
exclude = ['active']
# Filtering Choices
def __init__(self, *args, **kwargs):
super(CreateVesselForm, self).__init__(*args, **kwargs)
# Filtering just Free Slots
self.fields['slot'].queryset = Slot.objects.filter(is_free=True)
# Filtering just Free Storages
self.fields['storage'].queryset = Storage.objects.filter(is_free=True)
The Slot Field is a ForeignKey and the Storage Field is a ManytoMany Field.
In my views.py, by the time I save this form I change the status of "is_free" to False. However when the time to edit this item(Vessel) - getting it from a form instance - the options already selected before, no longer appear in the form fields because my queryset is filtering just by status=True.
The perfect form queryset for me would be:
for ForeignKey
the current selected item "vessel.slot" + Slot.objects.filter(is_free=True) ?
for ManytoMany
the current selected item "vessel.storage" + Storage.objects.filter(is_free=True) ?
Is there a way to get it done ?
You can try like this:
class CreateVesselForm(forms.ModelForm):
class Meta:
model = Vessel
exclude = ['active']
# Filtering Choices
def __init__(self, *args, **kwargs):
super(CreateVesselForm, self).__init__(*args, **kwargs)
if kwargs['instance']:
self.fields['storage'].queryset = kwargs['instance'].storage.all()|Storage.objects.filter(is_free=True)
self.fields['slot'].queryset = Slot.objects.filter(pk=kwargs['instance'].slot.pk)|Slot.objects.filter(is_free=True)
I am building a filter for my website where people can filter by cuisine. In order to achieve this I used a model form to receive input information that sets the filter variable in a query in my view. However as you can see in the image linked below, the default select for my cuisine categories is '-------' .
How would I go about changing this to say the words 'all' and setting a value so my filter queries everything for those categories? I think it has something to do with using a form method but I have been unable to understand what is actually happening in some of the examples.
Here is my simple code
Models
class Cuisine(models.Model):
name = models.CharField()
def __str__(self):
return self.name
class Food(models.Model):
name = models.CharField()
cuisine = models.ForeignKey(Cuisine)
def __str__(self):
return self.name
Views
def home_page(request):
if request.method == 'GET':
form = FilterForm(request.GET)
if form.is_valid():
cuisine = form.cleaned_data['cuisine']
food = get_list_or_404(Food, cuisine__pk=cuisine.pk)
return render('base.html', {'food': food, 'form':form})
else:
form = FilterForm()
return render('base.html', {'form':form})
Form
class FilterForm(forms.ModelForm):
class Meta:
model = Cuisine
fields = ('name')
I wouldn't use a modelform here. You only have one field, and you're not using it to create or edit instances of Food or Cuisine. It would be simpler to use a manual form with a ModelChoiceField, to which you can pass the empty_label parameter.
class FilterForm(forms.Form):
cuisine = forms.ModelChoiceField(queryset=Cuisine.objects.all(),
empty_label="All")
(Note you could do this with the ModelForm as well, but that just makes it even more pointless, as you are now not using any of the ModelForm functionality.)