During form processing I'd like to be able to set a foreign key field on a model object without the user having to select the key from a dropdown.
For instance:
#models.py
class AAA(models.Model):
some_field = models.TextField()
class BBB(models.Model):
another_field = models.TextField()
key_field = models.ForeignKey('AAA')
The user will navigate from a view showing an instance of 'AAA' to a create_object style view that will create an instance of 'BBB' given a parameter referring to 'AAA'. The foreign key is set in code to point back to the 'AAA' instance.
The django comments framework seems to do this but I can't figure out how.
Any ideas? I'm sure it should be quite simple.
You can exclude the key_field from your model form, save with commit=False, then set key_field in your view before saving to the database.
class BBBForm(forms.ModelForm):
class Meta:
model = BBB
exclude = ("key_field",)
def create_view(request, **kwargs):
if request.method == "POST":
aaa = # get aaa from url, session or somewhere else
form = BBBForm(request.POST)
if form.is_valid():
bbb = form.save(commit=False)
bbb.key_field = aaa
bbb.save()
return HttpResponseRedirect("/success-url/")
...
As the user creates a BBB via an instance of AAA, this should be reflected in the URL, i.e., your "create_object style view" will get a parameter identifying an AAA object. You can use it to get the object from the database and create your BBB object accordingly:
from django.shortcuts import get_object_or_404
def create_bbb_view(request, aaa_id):
a = get_object_or_404(AAA, id=aaa_id)
form = MyBBBCreationForm(request.POST) # or similar code
if form.is_valid():
b = BBB.objects.create(key_field=a) # plus other data from form
# ...
(You could also set key_field to aaa_id directly, but it's probably a good idea to check if the object exists.)
Related
I want to hide the column bh in the html page so I am hiding it by exclude in forms.py and trying to set foreign key column bh with request.user.username in the views but it is giving me this error:
Cannot assign "]>": "Bed.bh" must be a "Hospital" instance.
Is there any way to resolve the issue. Please help!
`#forms.py
class BedForm(forms.ModelForm):
class Meta:
model=Bed
fields = ('bed_id','bed_type','created_date','bh',)
exclude=('bh',)
if request.method == "POST":
form = BedForm(request.POST)
if form.is_valid():
bed = form.save(commit=False)
bed.created_date = timezone.now()
hh = Hospital.objects.filter(hospital_id=request.user.username)
bed.bh=hh
bed.save()
b = Bed.objects.filter(bh=request.user.username)
`
This is because Hospital.objects.filter() is returning a QuerySet, not an instance of Hospital.
You want something like:
hh = Hospital.objects.filter(hospital_id=request.user.username)[0]
or
hh = Hospital.objects.get(hospital_id=request.user.username)
In both cases, however you will have to either check that the object exists before retrieving it, or catch the exception, then figure out what to do next.
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()
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.)
I have a model Calendar and in a form I want to be able to create multiple instances of it.
Here are my models:
class Event(models.Model):
user = models.ForeignKey(User)
class Group(models.Model):
name = models.CharField(_('Name'), max_length=80)
events = models.ManyToManyField(Event, through='Calendar')
class Calendar(models.Model):
event = models.ForeignKey(Event)
group = models.ForeignKey(Group)
class CalendarInline(admin.TabularInline):
model = Calendar
extra = 1
class GroupAdmin(admin.ModelAdmin):
inlines = (CalendarInline,)
Here is how I try to code my form:
class AddEventToGroupForm(ModelForm):
group = ModelMultipleChoiceField(queryset=Group.objects.all(), widget=SelectMultiple())
def save(self):
for g in self:
g.save()
class Meta:
model = Calendar
fields = ('group',)
And here is a part of my view:
e = Event.objects.get(id=event_id)
calentry = Calendar(event=e)
if request.POST:
f = AddEventToGroupForm(data=request.POST, instance=calentry)
if f.is_valid():
f.save()
If I try to submit that form, I get:
AttributeError at /groups/add_event/7/
'BoundField' object has no attribute 'save'
What is the proper way to create multiple instances of Calendar in this
situation?
That's not how to deal with many-to-many relationships in forms. You can't iterate through fields in a form and save them, it really doesn't work that way.
In this form, there's only one field, which happens to have multiple values. The thing to do here is to iterate through the values of this field, which you'll find in the cleaned_data dictionary (when the form is valid).
So, in your view, you do something like:
if f.is_valid():
for group in f.cleaned_data['group']:
calentry.groups.add(group)
Note you're not 'saving' the AddEventToGroupForm form at all. I would make it a standard forms.Form, rather than a ModelForm, as you're not really depending on any of the ModelForm functionality.