How can you update certain model fields when a form is posted using Django's UpdateView?
When the user checks complete = True on the form, and submits it, I want their name and date to be saved to this record (fields not visible on the form). The code below isn't throwing any errors, but it also isn't updating the fields requested.
Is it potentially because the view is already updating this record with the form, so they conflict?
view:
class maintenanceEdit(LoginRequiredMixin,UpdateView,):
model = Maintenance
form_class = EditMaintenance
template_name = 'maintenance_edit.html'
login_url = 'login'
success_url = reverse_lazy('equipmentdashboard')
def form_valid(self, form,):
instance = form.save(commit=False)
instance.user = self.request.user
user = instance.user.first_name +" "+instance.user.last_name
completed = form.instance.completed
dateCompleted = form.instance.dateCompleted
if (dateCompleted is None):
if completed == True:
updateMaintenance = Maintenance.objects.get(id = instance.id)
updateMaintenance.dateCompleted = timezone.now()
updateMaintenance.completedBy = user
updateMaintenance.save(update_fields=['dateCompleted','completedBy',])
return super(maintenanceEdit, self).form_valid(form)
model:
class Maintenance(models.Model):
device = models.ForeignKey(get_user_model(),on_delete=models.CASCADE,)
customerTag = models.CharField(max_length=50,)
maintenanceItem = models.CharField(max_length=35,blank=True,)
maintenanceDescrip = models.TextField(max_length=300,blank=True,)
maintenanceNotes = models.TextField(max_length=300,blank=True,)
dateCreated = models.DateTimeField(auto_now_add=True,)
dateDue = models.DateTimeField(auto_now=False, auto_now_add=False, null=True, blank=True, editable=True)
dateCompleted = models.DateTimeField(auto_now=False, auto_now_add=False, null=True, blank=True, editable=True)
completed = models.BooleanField(default = False)
createdBy = models.CharField(max_length=35,blank=True,)
completedBy = models.CharField(max_length=35,blank=True,)
form:
class EditMaintenance(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(EditMaintenance, self).__init__(*args, **kwargs)
self.fields['maintenanceItem'].required = True
self.fields['dateDue'].required = True
class Meta:
model = Maintenance
fields = ['maintenanceItem','dateDue','maintenanceDescrip',
'maintenanceNotes','completed',]
labels = {
'maintenanceItem': ('Maintenance Item'),
'dateDue': ('Maintenance Due'),
'maintenanceDescrip': ('Maintenance Description'),
'maintenanceNotes': ('Maintenance Notes'),
'completed': ('Complete Maintenance'),
}
I also tried, in my view:
def form_valid(self, form,):
instance = form.save(commit=False)
instance.user = self.request.user
user = instance.user.first_name +" "+instance.user.last_name
completed = form.instance.completed
dateCompleted = form.instance.dateCompleted
if (dateCompleted is None):
if completed == True:
Maintenance.objects.filter(id = instance.id).update(dateCompleted = timezone.now(),completedBy = user)
return super(maintenanceEdit, self).form_valid(form)
Related
Model.py
class Branch(models.Model): # Branch Master
status_type = (
("a",'Active'),
("d",'Deactive'),
)
name = models.CharField(max_length=100, unique=True)
suffix = models.CharField(max_length=8, unique=True)
Remark = models.CharField(max_length=200, null=True, blank=True)
created_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
create_at = models.DateTimeField(auto_now_add=True)
update_at = models.DateTimeField(auto_now=True)
status = models.CharField(max_length=1, choices = status_type, default = 'a')
def __str__(self):
return self.name
class Vendor(models.Model):
status_type = (
("a",'Active'),
("d",'Deactive'),
)
branch = models.ManyToManyField(Branch)
company = models.CharField(max_length=200)
name = models.CharField(max_length=200)
phone = models.CharField(max_length=11, unique = True)
email = models.EmailField(max_length=254, unique = True)
gst = models.CharField(max_length=15, unique = True)
pan_no = models.CharField(max_length=10, unique = True)
add_1 = models.CharField(max_length=50, null=True, blank = True)
add_2 = models.CharField(max_length=50, null=True, blank = True)
add_3 = models.CharField(max_length=50, null=True, blank = True)
Remark = models.CharField(max_length=200, null=True, blank=True)
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
create_at = models.DateTimeField(auto_now_add=True)
update_at = models.DateTimeField(auto_now=True)
status = models.CharField(max_length=1, choices = status_type, default = 'a')
def __str__(self):
return self.company
form.py
i want save like created_by field
class VendorForm(ModelForm):
class Meta:
model = Vendor
fields = 'all'
exclude = ['created_by', 'branch']
widgets = {
'company':forms.TextInput(attrs={'class':'form-control'}),
'name':forms.TextInput(attrs={'class':'form-control'}),
'phone':forms.TextInput(attrs={'class':'form-control'}),
'email':forms.EmailInput(attrs={'class':'form-control'}),
'gst':forms.TextInput(attrs={'class':'form-control'}),
'pan_no':forms.TextInput(attrs={'class':'form-control'}),
'add_1':forms.TextInput(attrs={'class':'form-control'}),
'add_2':forms.TextInput(attrs={'class':'form-control'}),
'add_3':forms.TextInput(attrs={'class':'form-control'}),
'Remark':forms.Textarea(attrs={'class':'form-control','rows':'2'}),
'status':forms.Select(attrs={'class':'form-control'}),
}
Views.py
I have pass branch in session.
I want to save with branch which is many to many field
def Add_Vendor(request): # for vendor add
msg = ""
msg_type = ""
branch_id = request.session['branch_id']
branch_data = Branch.objects.get(id = branch_id)
form = ""
if request.method == "POST":
try:
form = VendorForm(request.POST)
if form.is_valid:
vendor_add = form.save(commit=False)
vendor_add.created_by = request.user
vendor_add.instance.branch = branch_data.id
vendor_add.save()
form.save_m2m() # for m to m field save
msg_type = "success"
msg = "Vendor Added."
form = VendorForm(initial={'branch':branch_id})
except:
msg_type = "error"
msg = str(form.errors)
print(msg)
else:
form = VendorForm(initial={'branch':branch_id})
context = {
'form':form,
'branch_data':branch_data,
'msg_type':msg_type,
'msg':msg,
'btn_type':'fa fa-regular fa-plus',
'form_title':'Vendor Form',
'tree_main_title':'Vendor',
'v_url':'vendor_page',
'tree_title':'Add Form',
}
return render(request, 'base/vendor_master/form_vendor.html',context)
I would advise not to work with commit=False in the first place:
def Add_Vendor(request): # for vendor add
branch_id = request.session['branch_id']
branch_data = get_object_or_404(Branch, pk=branch_id)
if request.method == 'POST':
form = VendorForm(request.POST, request.FILES)
if form.is_valid():
form.instance.created_by = request.user
form.instance.branch = branch_data.id
vendor_add = form.save()
vendor_add.branch.add(branch_data)
return redirect('name-of-some-view')
else:
form = VendorForm()
context = {
'form': form,
'branch_data': branch_data,
'btn_type': 'fa fa-regular fa-plus',
'form_title': 'Vendor Form',
'tree_main_title': 'Vendor',
'v_url': 'vendor_page',
'tree_title': 'Add Form',
}
return render(request, 'base/vendor_master/form_vendor.html', context)
You can simplify your form by automatically adding form-control to each widget:
class VendorForm(ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.fields.values():
attrs = field.widget.attrs
attrs['class'] = attrs.get('class', '') + ' form-control'
class Meta:
model = Vendor
exclude = ['created_by', 'branch']
Note: In case of a successful POST request, you should make a redirect
[Django-doc]
to implement the Post/Redirect/Get pattern [wiki].
This avoids that you make the same POST request when the user refreshes the
browser.
Note: You can set a field editable=False [Django-doc]. Then the field does not show up in the ModelForms and ModelAdmins by default. In this case for example with created_by.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Note: Please do not pass messages manually to the template. Django has the messages framework [Django-doc], which allows to add messages to the request, which will then be delivered the next time a template renders these messages. This makes delivering multiple messages convenient, as well as setting different log levels to the messages.
I have a specific problem with my forms. I think it would be better to share my codes instead of explaining the problem in detail.
However, to explain in a nutshell; inside my model I have field OneToOneField and model of that field has inlineformset_factory form. My new model also has a form and I want to save both forms.
I get the following error when I want to save the offer update form:
TypeError at /ru/mytarget/offer-update/T2GTTT053E9/
AdminOfferUpdateView.form_invalid() missing 2 required positional arguments: 'request_form' and 'request_item_formset'
Models:
request.py
class RequestModel(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name="customer_requests")
id = ShortUUIDField(primary_key=True, length=10, max_length=10, prefix="T", alphabet="ARGET0123456789", unique=True, editable=False)
status = models.BooleanField(default=True)
request_title = models.CharField(max_length=300)
......
updated_on = models.DateTimeField(auto_now=True)
published_date = models.DateTimeField(default=timezone.now)
def __str__(self):
return str(self.request_title)
class Meta:
verbose_name_plural = "Requests"
verbose_name = "Request"
def get_absolute_url(self):
return reverse('mytarget:customer_request_details', kwargs={'pk': self.id})
class RequestItem(models.Model):
request_model = models.ForeignKey(RequestModel, on_delete=models.CASCADE, related_name="request_items")
product_name = models.CharField(max_length=300)
......
offer.py
class OfferModel(models.Model):
request_model_name = models.OneToOneField(RequestModel, on_delete=models.CASCADE, primary_key=True)
status = models.BooleanField(default=True)
offer_validity = models.CharField(max_length=50, blank=True)
......
updated_on = models.DateTimeField(auto_now=True)
published_date = models.DateTimeField(default=timezone.now)
def __str__(self):
return str(self.request_model_name)
class Meta:
verbose_name_plural = "Offers"
verbose_name = "Offer"
def get_absolute_url(self):
return reverse('mytarget:admin_offer_update', kwargs={'pk': self.request_model_name})
Forms:
request_create_form.py
class CustomerRequestForm(forms.ModelForm):
disabled_fields = ("customer",)
class Meta:
model = RequestModel
fields = ("customer", "request_title", "delivery_time", "shipping_country", "shipping_address",
"preferred_currency", "shipping_term", "delivery_term")
widgets = {
'request_title': TextInput(attrs={'class': 'form-control tableFormInputs',
'placeholder': _('Example: Printers, Toner, and Cartridges')}),
......
}
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('customer')
super(CustomerRequestForm, self).__init__(*args, **kwargs)
self.fields['preferred_currency'].queryset = self.fields['preferred_currency'].queryset.translated().order_by("translations__currency_name")
self.fields['shipping_term'].queryset = self.fields['shipping_term'].queryset.translated().order_by("translations__shipping_term")
for field in self.disabled_fields:
self.fields[field].widget = forms.HiddenInput()
self.fields[field].disabled = True
class CustomerRequestItemForm(forms.ModelForm):
class Meta:
model = RequestItem
fields = ("product_name", "product_info", "target_price", "price", "quantity", "dimensions", "net_weight", "gross_weight",
"hs_or_tn_ved_code", "brand", "manufacturer", "origin_country", "manufacturer_address")
exclude = ()
widgets = {
'product_name': TextInput(attrs={'class': 'form-control tableFormInputs'}),
......
}
RequestItemInlineFormset = inlineformset_factory(RequestModel, RequestItem,
form=CustomerRequestItemForm,
extra=1,
can_delete=True
)
offer_update_form.py
class AdminOfferUpdateForm(forms.ModelForm):
disabled_fields = ()
hidden_fields = ("request_model_name",)
request_title = forms.CharField(required=False, widget=TextInput(attrs={'class': 'form-control tableFormInputs', 'placeholder': _('Example: Printers, Toner, and Cartridges')}))
......
class Meta:
model = OfferModel
fields = ("request_model_name", "offer_validity", ......
)
widgets = {'offer_validity': TextInput(attrs={'class': 'form-control tableFormInputs'}),
......
'is_detailed_offer': CheckboxInput(attrs={'class': 'form-check-input'}),
}
def __init__(self, *args, **kwargs):
super(AdminOfferUpdateForm, self).__init__(*args, **kwargs)
self.fields["preferred_currency"].choices = [(c.id, c.currency_name) for c in Currency.objects.all()]
self.fields["shipping_term"].choices = [(st.id, st.shipping_term) for st in ShippingTerm.objects.all()]
self.fields["delivery_term"].choices = [(dt.id, dt.delivery_term) for dt in DeliveryTerms.objects.all()]
self.fields["request_statuses"].choices = [(r.id, r.status) for r in RequestStatus.objects.all()]
for field in self.disabled_fields:
self.fields[field].disabled = True
for field in self.hidden_fields:
self.fields[field].widget = forms.HiddenInput()
Views:
offer_update_view.py
#method_decorator([login_required(login_url=reverse_lazy("accounts:signin")), user_is_superuser], name='dispatch')
class AdminOfferUpdateView(UpdateView):
model = OfferModel
form_class = AdminOfferUpdateForm
template_name = "mytarget/admin_offer_update.html"
def get_context_data(self, **kwargs):
context = super(AdminOfferUpdateView, self).get_context_data(**kwargs)
if self.request.POST:
context['request_form'] = AdminOfferUpdateForm(self.request.POST, instance=self.object.request_model_name)
context['request_item_formset'] = RequestItemInlineFormset(self.request.POST, instance=self.object.request_model_name)
else:
context['request_form'] = AdminOfferUpdateForm(instance=self.object.request_model_name)
context['request_item_formset'] = RequestItemInlineFormset(instance=self.object.request_model_name)
return context
def form_valid(self, form):
context = self.get_context_data()
request_form = context['request_form']
request_item_formset = context['request_item_formset']
with transaction.atomic():
self.object = form.save()
if request_form.is_valid() and request_item_formset.is_valid():
request_form.instance = self.object.request_model_name
request_form.save()
request_item_formset.instance = self.object.request_model_name
request_item_formset.save(commit=False)
for ri in request_item_formset:
ri.save(commit=False)
request_item_formset.save()
return super(AdminOfferUpdateView, self).form_valid(form)
def form_invalid(self, form, request_form, request_item_formset):
return self.render_to_response(
self.get_context_data(form=form, request_form=request_form, request_item_formset=request_item_formset)
)
def get_initial(self):
self.object = self.get_object()
if self.object:
return {"request_model": self.object.request_model_name, "request_item_formset": self.object.request_model_name}
return super().initial.copy()
def get_success_url(self):
return reverse('mytarget:admin_offer_update', kwargs={'pk': self.object.id})
I solved my problem. I created a button function that creates a new model with inheritance of other model fields. In this way, there is no need to edit the form of the other model inside the form of my current model.
I don't understand why my test doesn't work. I have a page with product. There is a form with button 'Buy product'. After pushing this button, if a client have enough money, the item will bought and the amount of money in the account will change. But in my test the amount of money will be the same after buying a product, although the object(purchased item) will be created.
Purchased item model:
class BoughtItem(models.Model):
name = models.CharField(max_length=100, verbose_name='Название товара или акции', blank=True)
price = models.IntegerField(verbose_name='Цена', blank=True)
created_at = models.DateTimeField(auto_now_add=True)
Profile model:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
money = models.IntegerField(default=0)
form:
class BuyForm(forms.ModelForm):
class Meta:
model = BoughtItem
fields = ('name',)
widgets = {'name': forms.HiddenInput()}
view:
class ItemDetailView(generic.DetailView):
model = Item
template_name = 'item_detail.html'
context_object_name = 'item'
def get_object(self, queryset=None):
return get_object_or_404(Item, pk=self.kwargs.get('pk'))
def post(self, request, *args, **kwargs):
buy_form = BuyForm(request.POST)
if buy_form.is_valid():
purchase = buy_form.save(commit=False)
item = self.get_object()
user = Profile.objects.get(user__username=request.user.username)
if user.money >= item.price:
sum_difference = user.money - item.price
user.money = sum_difference
user.save()
purchase.name = item.name
purchase.price = item.price
purchase.save()
return HttpResponseRedirect(reverse('account', kwargs={'username': request.user.username}))
else:
return HttpResponseRedirect(reverse('purchase_error'))
else:
return render(request, 'item_detail.html',
context={'buy_form': buy_form, 'object': self.get_object()})
urls:
urlpatterns = [
path('shop_list/', ShopListView.as_view(), name='shop_list'),
path('<str:name>', ItemListView.as_view(), name='shop_detail'),
path('item/<int:pk>', ItemDetailView.as_view(), name='item_detail'),
path('purchase_error/', purchase_error, name='purchase_error'),
]
test:
class ShopTest(TestCase):
#classmethod
def setUpTestData(cls):
Shop.objects.create(
name='Samsung',
description='Магазин электроники',
)
Item.objects.create(
name='Телефон Samsung A50',
description='Описание телефона',
price=20000,
shop_id=Shop.objects.get(name='Samsung').pk
)
user = User.objects.create(username='testuser')
user.set_password('12345')
user.save()
Profile.objects.create(
user=user,
money=100000
)
def test_if_change_field_money_after_purchase(self):
self.client.login(username='testuser', password='12345')
user = Profile.objects.get(user__username='testuser')
item = Item.objects.get(pk=1)
self.client.post(reverse('item_detail', kwargs={'pk': item.pk}))
self.assertNotEquals(user.money, 100000)
The changes have been saved to the DB but your object still contains the old data, call instance.refresh_from_db() to get the latest data from the DB
def test_if_change_field_money_after_purchase(self):
self.client.login(username='testuser', password='12345')
user = Profile.objects.get(user__username='testuser')
item = Item.objects.get(pk=1)
user.refresh_from_db()
self.assertNotEquals(user.money, 100000)
model 1
class Products(models.Model):
product_category = models.ForeignKey(ProductCategory)
product_sub_category = models.ForeignKey(ProductCategory)
product_name = models.CharField(max_length = 200)
is_active = models.BooleanField(default = True)
and so on...
model 2
class ProductImages(models.Model):
product = models.ForeignKey( Products )
product_image = models.FileField(_('Attachment'), upload_to='attachments')
is_active = models.BooleanField(default = True)
CreateView
class ProductCreate(CreateView):
model = Products
template_name = "products/product_add.html"
fields = ['product_category', 'product_sub_category', 'product_name', 'size', 'color', 'price', 'price_info', 'description_1', 'description_2', 'about_product', 'features', 'specification']
success_url = "products/product-list"
def form_valid(self, form):
product_form = form.save(commit = False)
# **expecting product_form.id to be non None**
if 'product_images' in self.request.FILES:
for img in self.request.FILES.getlist('product_images'):
ProductImages(product = product_form, product_image = img).save()
super(ProductCreate, self).post(request, *args, **kwargs)
Problem statement :
form.save( commit = False ) returning Product instance but product id is None. Is it illegal to expect object id as object is not yet saved to db?
Yes. The id is allocated by the database, and since commut=False explicitly means "don't send to the db", it won't have an id.
Can I get the value of the field which I am not showing in form? I want to pass ref_id in session.
This is my model:
def _createId():
"""
"""
return hexlify(os.urandom(4))
class jobpost(models.Model):
item_types = (
('Full Time','Full Time'),
('Part Time','Part Time'),
('Contract','Contract'),
)
posttype= (
('Job','Job'),
('Classified','Classified'),
('Project/Task','Project/Task'),
('Internship','Internship'),
)
#user = models.ForeignKey(User)
job_id = models.AutoField(primary_key=True)
country= models.ForeignKey(Country,to_field = 'country_name', null=True)
#user = models.ForeignKey(User, editable = False)
post_type = models.CharField(max_length=255,null=True, choices=posttype,default='Job')
job_type = models.CharField(max_length=255,null=True, choices=item_types,default='Full Time')
job_location = models.CharField(max_length=255,null=True)
job_title = models.CharField(max_length=255,null=True)
job_description = models.TextField(null=True)
start_date = models.DateField(null=True, help_text="mm/dd/yyyy")
end_date = models.DateField(null=True, help_text="mm/dd/yyyy")
how_to_apply = models.CharField(max_length=255,null=True)
ref_id = models.CharField(max_length=32, default=_createId)
def __unicode__(self):
return unicode(self.country)
return self.post_type
return self.job_location
return self.job_type
return self.job_title
return self.job_description
return self.start_date
return self.end_date
return self.how_to_apply
return self.ref_id
means i am not displaying it in my form and i want to pass this value in session in next form..
can anyone tell me how can i do this? and how can i pass the primary key of the form in next form ?
forms.py
class JobPostForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(JobPostForm, self).__init__(*args, **kwargs)
self.fields['ref_id'].widget = forms.HiddenInput()
class Meta:
model = jobpost
views.py
def your_view(request):
if request.method == 'POST':
form = JobPostForm(request.POST)
if form.is_valid():
request.session['ref_id'] = form.cleaned_data.get('ref_id')
pk = form.save()
request.session['pk'] = pk.id
else:
form = JobPostForm()
return render(request, page.html,{'form': form})