How do I update a non existing related object through Django model forms ?
I have two objects: Participant and Emergency. Emergency is a child of participant like if run the query: participant = ParticipantInfo.objects.get(pk = prk) I can access emergency = participant.emergency.
I cannot update emergency with data from a form using a POST request.
Can anyone help me please.
Thanks
Here's my models.py for clarity.
models.py
class EmergencyInfo(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
phone_number = models.CharField(max_length=50)
email = models.EmailField(max_length=100, blank=True, verbose_name="Emergency Contact Email")
relationship = models.CharField(max_length=100)
class ParticipantInfo(models.Model):
first_name = models.CharField(max_length=100)
middle_initial = models.CharField(max_length=1, blank=True)
emergency = models.ForeignKey(EmergencyInfo, on_delete = models.CASCADE, editable= False, null=True, blank=True)
views.py
def update_participant(request, pk):
# this function comes after update_specific
if request.method == "GET":
forms = get_participant_form_data(pk)
context = {'forms': forms, 'pk': pk}
return render(request, 'core/participantinfo_update_form.html', context)
if request.method == "POST":
return update_specific_form(request, pk)
def update_specific_form(request, pk):
participant = ParticipantInfo.objects.get(pk = pk)
# if the object didn't exist create it like normal
if participant.emergency is None:
emergencyform =EmergencyForm(request.POST)
if (emergencyform.is_valid):
emergencyform.save()
messages.success(request, 'saved')
return redirect(request.path_info)
# if the object exists, update it
if participant.emergency is not None:
emergencyform = EmergencyForm(request.POST, instance = participant.emergency)
if (emergencyform.is_valid):
emergencyform.save()
messages.success(request, 'saved')
return redirect(request.path_info)
Your problem seems to be with the is_valid method. Replace it with is_valid(). So your lines would be:
if (emergencyform.is_valid()):
#code
I have found an answer. Not only I needed to call the is_valid() instead of is_valid
but I also needed to tie the created object with it's parent like:
Save the new child object
Save the parent objects (updates the null
foreign key to a value)
*#get the existing parent object*
participant = ParticipantInfo.objects.get(pk = pk)
*#if no child object exists*
if participant.emergency is None:
emergencyform =EmergencyForm(request.POST)
if (emergencyform.is_valid()):
emergency = emergencyform.save(commit=False)
participant.emergency = emergency
emergencyform.save()
participant.save()
messages.success(request, 'saved')
return redirect(request.path_info)
Related
I have a form where I want request.user to populate as little as possible and rely on the views to populate other fields automatically.
As a result, some of these fields are not rendered on the form.
The code in my view seems to work fine for the FK relationship, but some reason the m2m is failing.
It's probably the first time I am trying to save a form with m2m and I am probably missing something.
At the moment the error I get with the current code is 'VoucherForm' object has no attribute 'user'.
If I remove voucherform.user.add(userprofile)from the views the form will save, but will not add the user.
model
class UserProfile(models.Model):
user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
class Voucher(models.Model):
user = models.ManyToManyField(User, blank=True)
venue = models.ForeignKey(Venue, blank=True, null=True, related_name="vouchervenues", on_delete=models.CASCADE)
title = models.TextField('voucher title', blank=True)
terms = models.TextField('terms & conditions', blank=True)
form
class VoucherForm(ModelForm):
class Meta:
model = Voucher
fields = ('title','terms')
labels ={
'title': '',
'terms': '',
}
widgets = {
'title': forms.TextInput(attrs={'class':'form-control', 'placeholder':'Enter title'}),
'terms': forms.TextInput(attrs={'class':'form-control', 'placeholder':'Enter terms'}),
}
views
def add_voucher(request, userprofile_id):
url = request.META.get('HTTP_REFERER')
venue = UserProfile.objects.filter(user=request.user).values('venue')
userprofile = UserProfile.objects.get(id=userprofile_id)
submitted = False
if request.method =="POST":
voucherform = VoucherForm(request.POST)
if voucherform.is_valid():
data = voucherform.save(commit=False)
data.user_id = userprofile.id
data.venue_id = venue
data.save()
voucherform.save_m2m()
voucherform.user.add(userprofile)
return HttpResponseRedirect(url)
else:
voucherform = VoucherForm
if 'submitted' in request.GET:
submitted=True
return redirect('venue-loyalty-card',{'submitted':submitted,'userprofile':userprofile})
Basically, the problem is that you haven't mentioned user field in VoucherForm at fields so it says 'VoucherForm' object has no attribute 'user', you can do the following:
from django.shortcuts import get_object_or_404
def add_voucher(request, userprofile_id):
url = request.META.get('HTTP_REFERER')
venue = UserProfile.objects.filter(user=request.user).values('venue')
userprofile = UserProfile.objects.get(id=userprofile_id)
submitted = False
if request.method =="POST":
voucherform = VoucherForm(request.POST)
if voucherform.is_valid():
data = voucherform.save(commit=False)
data.user_id = userprofile.id
data.venue_id = venue
data.save()
voucherform.save_m2m()
current_voucher_instance= get_object_or_404(Voucher,id=data.id)
current_voucher_instance.user.add(userprofile.id)
return HttpResponseRedirect(url)
else:
voucherform = VoucherForm
if 'submitted' in request.GET:
submitted=True
return redirect('venue-loyalty-card',{'submitted':submitted,'userprofile':userprofile})
Note: It is better to use get_object_or_404() than get() as it calls get() on a given model manager, but it raises Http404 instead of the model's DoesNotExist exception.
I have created two models Leads and Deals, and I have coded some logic such that if you click a button the Lead becomes a Deal, so what I want it is that a new form is presented to the user but that form already contains the information from the Leads model.
#login_required
def close_lead(request):
if request.method == 'POST':
deal_form = DealForm(request.POST)
if deal_form.is_valid():
deal_form.save()
messages.success(request, 'You have successfully updated the status from open to Close')
id = request.GET.get('project_id', '')
obj = Leads.objects.get(project_id=id)
obj.status = "Closed"
obj.save(update_fields=['status'])
return HttpResponseRedirect(reverse('dashboard'))
else:
messages.error(request, 'Error updating your Form')
else:
id = request.GET.get('project_id', '')
obj = get_object_or_404(Leads, project_id=id)
print(obj.expected_revenue)
form = NewDealForm(request.POST or None, instance=obj)
return render(request,
"account/close_lead.html",
{'form':form})
I have done some debug and printed to the console the queryset and the information is fine, so the queryset is no the problem, the problem is that the NewForm doesn't prepopulate the new values.
models.py (only 2 models shown)
class Leads(models.Model):
CHOICES = (
('Illumination Studies','Illumination Studies'),
('Training','Training'),
('Survey Design','Survey Design'),
('Software License','Software License')
)
STATUS = (('Open','Open'),
('Closed','Closed'),
('Canceled', 'Canceled')
)
project_id = models.BigAutoField(primary_key=True)
company = models.ForeignKey(Company, on_delete=models.CASCADE)
agent = models.ForeignKey(Profile, on_delete=models.CASCADE, default="agent")
created_at = models.DateTimeField(auto_now_add=True)
point_of_contact = models.ForeignKey(Client, on_delete=models.CASCADE)
expected_revenue = MoneyField(max_digits=14, decimal_places=2, default_currency='USD')
expected_licenses = models.IntegerField(blank=True)
country = CountryField(blank_label='(select country)')
status = models.CharField(max_length=10,choices=STATUS)
estimated_closing_date = models.DateField(blank=True)
services = models.CharField(max_length=20,choices=CHOICES)
def __str__(self):
return f'{self.company}'
class Deal(models.Model):
project_id = models.ForeignKey(Leads, on_delete=models.CASCADE, default='id')
agent = models.ForeignKey(Profile, on_delete=models.CASCADE, default="agent")
service = models.ForeignKey(Leads, on_delete=models.CASCADE, related_name='service')
closing_date = models.DateField(auto_now_add=True)
client = models.ForeignKey(Client, on_delete=models.CASCADE,default='client')
licenses = models.ForeignKey(Leads,on_delete=models.CASCADE, related_name='licenses')
revenue = MoneyField(max_digits=14, decimal_places=2, default_currency='USD')
comments = models.TextField(blank=True,null=True)
Now, it could be that I have to inherit from a different form?
forms.py (only NewDealForm)
class NewDealForm(forms.ModelForm):
class Meta:
model = Deal
fields = ['agent','client','project_id','service', 'licenses','revenue', 'comments']
Obviously, worst-case scenario is to create a dictionary to extract the data from the queryset and then pass it to the form, but I'm sure Django has a more elegant way to handle this process.
Well, I guess sometimes Stack Overflow pushes you to solve your own issues, this is the solution.
Essentially, the initial=queryset value was not initializing the form mainly because I have very specific relationships in my model, so what I did is to create a dictionary (key:value) with the form field as key, and my queryset from my model as the value, the code is as below:
'''
def close_lead(request):
if request.method == 'POST':
deal_form = DealForm(request.POST)
if deal_form.is_valid():
deal_form.save()
messages.success(request, 'You have successfully updated the status from open to Close')
id = request.GET.get('project_id', '')
obj = Leads.objects.get(project_id=id)
obj.status = "Closed"
obj.save(update_fields=['status'])
return HttpResponseRedirect(reverse('dashboard'))
else:
messages.error(request, 'Error updating your Form')
else:
id = request.GET.get('project_id', '')
obj = get_object_or_404(Leads, project_id=id)
m = obj.__dict__
keys = Leads.objects.get(project_id=m['project_id'])
form_dict = {'project_id':keys.project_id,
'agent':keys.agent,
'client':keys.point_of_contact,
'company':keys.company,
'service':keys.services
}
form = NewDealForm(request.POST or None,initial = form_dict)
return render(request,
"account/close_lead.html",
{'form':form})
'''
As you can see, I create an object dictionary because the forms are different, so they share some common values not all, and then I simply adapt the dictionary, nice and easy, but I somehow expected that Django somehow finds relationships by name?, but maybe the batteries are not included for this.
I'm going to do my best not to sound like a real dummy, but no promises. I am a paramedic and I'm trying to make an app to document unit checks electronically.I have a model field that is foreign keyed to a few other models in my project. This field designates the unit the user is staffing for that day. I want the user to choose the unit he/she is staffing that day and have that information auto-fill any forms filled out for that session. I've tried storing the object using sessions and I get this "Object of type 'MedicUnit' is not JSON serializable". I've used the model_to_dict method and tried to pass the string of the unit name through the form_valid method but I get this "Cannot assign "'Medic 2'": "DailyCheck.medic_unit_number" must be a "MedicUnit" instance." I'm relatively new to programming and Django and this seems like a very easy problem to fix, but my google searching skills are not getting me anywhere. Here is my code:
Model.py for the origin of the unit_name model field
class MedicUnit(models.Model):
unit_name = models.CharField(max_length=50, default='')
is_active = models.BooleanField(default=True)
def __str__(self):
return self.unit_name
Model.py for one of the foreign key references to the unit_name
class DailyCheck(models.Model):
daily_user = models.ForeignKey(User, on_delete=models.PROTECT)
record_date = models.DateTimeField(auto_now=True)
medic_unit_number = models.ForeignKey('components.MedicUnit', related_name='medic_unit_number', on_delete=models.PROTECT, default='')
unit_property_number = models.ForeignKey('components.Vehicle', related_name='unit_property_number', on_delete=models.PROTECT, default='')
mileage = models.IntegerField(default=0)
narc_seal_number = models.IntegerField(default=0)
emergency_lights = models.BooleanField()
driving_lights = models.BooleanField()
red_bag = models.BooleanField()
LP_15 = models.BooleanField()
BLS_bag = models.BooleanField()
RTF_bag = models.BooleanField()
suction = models.BooleanField()
oxygen = models.BooleanField()
free_text = models.TextField(default='')
views.py for the directly above model
def check_home_view(request):
if request.method == 'POST':
form = ChooseMedicUnit(request.POST or None)
if form.is_valid():
unit_name = form.cleaned_data.get('medic_unit_number')
request.session['unit_name'] = model_to_dict(unit_name)
print(request.session['unit_name'])
return redirect('daily')
else:
form = ChooseMedicUnit()
return render(request, 'checks/checks_home.html', {'form':form})
class checkAdd(CreateView):
model = DailyCheck
fields = ['unit_property_number', 'mileage', 'narc_seal_number', 'emergency_lights', 'driving_lights', 'red_bag', 'LP_15', 'BLS_bag', 'RTF_bag', 'suction', 'oxygen', 'free_text']
success_url = '/checks'
def form_valid(self, form):
form.instance.daily_user = self.request.user
form.instance.medic_unit_number = self.request.session['unit_name']['unit_name']
return super().form_valid(form)
forms.py
class ChooseMedicUnit(forms.ModelForm):
class Meta:
model = DailyCheck
fields = ['medic_unit_number']
I think you can use MedicUnit.id. This should be sufficient to resolve the issue of initializing the field from the session in other forms:
def check_home_view(request):
if request.method == 'POST':
form = ChooseMedicUnit(request.POST or None)
if form.is_valid():
request.session['unit_name'] = form.cleaned_data.get('medic_unit_number').id # see here
print(request.session['unit_name'])
return redirect('daily')
else:
form = ChooseMedicUnit()
return render(request, 'checks/checks_home.html', {'form':form})
Thank you so much for the answer Andrey. I will try that too. I found that all I had to do was import the MedicUnit model to my view and change my form_valid method to the following:
def form_valid(self, form):
form.instance.daily_user = self.request.user
form.instance.medic_unit_number = MedicUnit.ojbects.get(pk=self.request.session['unit_name']['id'])
return super().form_valid(form)
Apparently sessions cannot store objects since after Django 1.5 I think. Someone may have to fact check me on that. So I referenced an instance of the object with a dictionary value from the model_to_dict data stored in the session with the MedicUnit.object.get call.
If you are having the same problem, you can print the session info to the terminal with a print statement just like in my check_home_view function view. I used that info to see what key was necessary to call primary key number.
I will check Andrey's solution later today and see how well that works. It seems a bit cleaner than my solution.
I created two models in my app: "Prescription" and "Prescription_status." When a user clicks save on the "New_prescription" modelform, I need to add a "Prescription_status" to the "Prescription."
For example, below I'd like to add 'Draft' status (PK=1). I don't want to set a default status. I've been trying everything, what am I missing?? Thanks in advance!
models.py
# Static Prescription Status Types
class Prescription_status(models.Model):
status = models.CharField(max_length=200)
status_definition = models.TextField()
def __str__(self):
return '%s' % (self.status)
# Prescription Model
class Prescription(models.Model):
order_id = models.AutoField(primary_key=True, unique=True)
status = models.ForeignKey(Prescription_status, models.SET_NULL, null=True)
I saved the following Prescription_status objects to the database, which I'd like to reference as users save or edit prescriptions:
status_id for "Draft" status = 1
status_id for "Ready for Signing" status = 2
status_id for "Signed and Authorized" status = 3
database chart showing PK for each status
forms.py
class PrescriptionForm(forms.ModelForm):
class Meta:
model = Prescription
fields = ('medication', 'quantity', 'directions', 'refills', 'earliest_permitted_fill_date', 'daw',)
widgets = {
'earliest_permitted_fill_date': DatePickerInput(), # default date-format %m/%d/%Y will be used
}
views.py
def new_rx(request):
if request.method == "POST":
form = PrescriptionForm(request.POST)
if form.is_valid():
prescription = form.save(commit=False)
prescription.status = Prescription_status.objects.get(pk=form.cleaned_data['1'])
prescription.save()
return redirect('home')
else:
form = PrescriptionForm()
return render(request, 'app/new_rx.html', {'form': form})
The above results in "NameError: name 'Prescription_status' is not defined." What is the right way to do this?
I'm trying to make a view where the user can edit DB records through a form in a template. I've searched a lot of web pages (and Django docs as well) where they teach how to make these views, but they always use the "id" that Django generates for each Model. In this particular Model, I have to use an AutoField to override the "id". Is there a way to use this AutoField as an "id" of the record with Django?
Here's my complete model:
class T031003 (models.Model):
C003IDCD = AutoField(primary_key=True)
C003INST = models.IntegerField(unique=True) #usar AutoSlug
C003TPCD = models.CharField(max_length=1)
C003CHCD = models.CharField(max_length=14)
C003MTR = models.CharField(max_length=30, blank=True, null=True)
C003CTCD = models.CharField(max_length=3)
C003RZSC = models.CharField(max_length=60, blank=True, null=True)
C003EML = models.EmailField(max_length = 254, blank=True, null=True)
C003LOGA = models.CharField(max_length=20)
C003LOGB = models.DateTimeField()
C003LOGD = models.CharField(max_length=15, blank=True, null=True)
C003LOGF = models.CharField(max_length=20, blank=True, null=True)
def __unicode__(self):
return '%s' % self.C003MTR
class T031003Form(ModelForm):
class Meta:
model = T031003
ordering = ["-C003MTR"]
exclude = ('C003LOGA','C003LOGB','C003LOGD','C003LOGE','C003LOGF')
And here's the view I tried to do, but it gives me the error "No T031003 matches the given query." and it's right, since there is no "id" in the table:
def t031003form_edit(request, id=None):
pin = get_object_or_404(T031003, pk=id)
form = T031003Form(request.POST or None, instance=pin)
if request.method == 'POST':
if form.is_valid():
form = form.save(False)
form.C003LOGA = request.user
form.C003LOGB = datetime.date.today()
form.C003LOGD = request.META['REMOTE_ADDR']
form.C003LOGF = request.META['USERDOMAIN']
form.save()
form = T031003Form()
else:
return HttpResponseRedirect('/erro/')
return render_to_response('T031003Form_edit.html', {'form': form,}, context_instance=RequestContext(request))
Any help would be very appreciated!
If a model has an AutoField — an auto-incrementing primary key — then that auto-incremented value will be calculated and saved as an attribute on your object the first time you call save():
>>> b2 = Blog(name='Cheddar Talk', tagline='Thoughts on cheese.')
>>> b2.id # Returns None, because b doesn't have an ID yet.
>>> b2.save()
>>> b2.id # Returns the ID of your new object.
There's no way to tell what the value of an ID will be before you call save(), because that value is calculated by your database, not by Django.
ref : https://docs.djangoproject.com/en/dev/ref/models/instances/?from=olddocs
Well, thanks to the help from a close friend, I could do the trick using formsets. Here's the view:
def t031002form_edit(request, id_auto):
j = get_object_or_404(T031002, pk=id_auto)
T031003FormSet = modelformset_factory(T031002, can_delete=True, max_num=1)
if request.method == 'POST':
form = T031002FormSet(request.POST or None, request.FILES or None, queryset=T031002.objects.filter(pk=id_auto))
if form.is_valid():
instance = form.save(commit=False)
form.C003LOGA = request.user
form.C003LOGB = datetime.date.today()
form.C003LOGD = request.META['REMOTE_ADDR']
form.C003LOGF = request.META['USERDOMAIN']
for reform in instance:
reform.save()
else:
return HttpResponseRedirect('/erro/')
else:
form = T031002FormSet(queryset=T031002.objects.filter(pk=id_auto))
return render_to_response(('T031002Form_edit.html'), {'form': form,}, context_instance=RequestContext(request))
So, with formsets, you can work nicely and with no worries. Hope it helps others with this same questioning.