Hello so I've a form and I'm trying to make it so that when that form is validated. But before it is saved. It will create a new instance of a EC_Node and save the command given in the Command_Form not only to the Command_Node but to the EC_Nodes. Which should have a manytoone relationship with the Command_Nodes and which should record all commands send to a command_node. But this will only work if when entering a command for the Command_Node I also capture that in a EC_Node. Which I don't know how to do exactly although I'm some ideas.
So any advice would be greatly appreciated. Relevant Code follows.
Relevant views.py
def update(request, host_id):
host_id = Command_Node.objects.get(pk=host_id)
form = Command_Form(request.POST or None, instance=host_id)
if form.is_valid():
# Original suggestion was command_form = Command_Form.objects.first()
command_form = form['host_id']
command_form.EC_Node_set.all() # <- not sure with all _ and Maj here
form.save()
return redirect('home')
return render (request, 'update.html', {'host_id':host_id,'form':form})
forms.py
class Command_Form(ModelForm):
class Meta:
model = Command_Node
fields = ('host_id','current_commands')
host_id = forms.ModelChoiceField(
required=True,
queryset=Beacon.objects.all(),
widget=forms.SelectMultiple(
attrs={
'class': 'form-control'
},
)
)
current_comamnds = forms.ChoiceField(
required=True,
choices=CHOICES
)
def save(self, **kwargs):
EC_Node.objects.create(
command=self.cleaned_data["current_commands"],
Command_node=self.instance
)
return super().save(**kwargs)
models.py
class Command_Node(models.Model):
host_id = models.ForeignKey(Beacon, on_delete=models.CASCADE)
current_commands = models.CharField(choices=CHOICES, max_length=50, null=True)
def __str__(self):
return str(self.host_id)
class EC_Node(models.Model):
Command_node = models.ForeignKey(Command_Node, on_delete=models.DO_NOTHING)
command = models.CharField(choices=CHOICES, max_length=50, null=True)
def __str__(self):
return str(self.Command_node)
When I try to update things, via the update-view the present error I'm getting is:
'BoundField' object has no attribute 'EC_Node_set'
Thus my question on here. Thanks.
Forms validated data are accessible in the Form.cleaned_data attribute. See the documentation.
So you need to access your user data using this dictionary:
def update(request, host_id):
host_id = Command_Node.objects.get(pk=host_id)
form = Command_Form(request.POST or None, instance=host_id)
if form.is_valid():
# Original suggestion was command_form = Command_Form.objects.first()
command_form = form.cleaned_data['host_id']
Related
My first post, really newbie at programming. I am having issues to update a field in a form. I'll try to explain my best.
I have a Catalog class (products or services) with a couple fields, 2 of them must be unique.
models.py
class Catalogo(models.Model):
item = models.CharField(
max_length=100, help_text="Product or service name", unique=True
)
sku = models.CharField(max_length=50, help_text="Part Number", unique=True)
category = models.CharField(
max_length=15, choices=categoria, verbose_name="Category"
)
description = models.CharField(
max_length=200,
help_text="Item description",
verbose_name="Descripción",
)
created = models.DateTimeField(auto_now_add=True, help_text="Created")
updated = models.DateTimeField(auto_now_add=True, help_text="Updated")
active = models.BooleanField(default=True)
class Meta:
verbose_name = "product and service"
verbose_name_plural = "products and services"
def __str__(self):
return self.item
Then i have a form to gather the information
forms.py
categories = [("Product", "Product"), ("Service", "Service")]
class NewProductForm(forms.Form):
item = forms.CharField(
widget=forms.TextInput(attrs={"class": "form-control"}),
label="Item",
max_length=100,
)
sku = forms.CharField(
widget=forms.TextInput(attrs={"class": "form-control"}),
label="Part number",
max_length=50,
)
category = forms.ChoiceField(
choices=categories,
label="Category",
)
description = forms.CharField(
widget=forms.Textarea(attrs={"class": "form-control"}),
label="Item description",
)
Now for the views...i created 2 functions, one for adding new product/service and one for updating
views.py
def new_prod_serv(request):
new_form = NewProductForm()
if request.method == "POST":
new_form = NewProductForm(request.POST)
if new_form.is_valid():
new_product = Catalogo(
item=new_form["item"].value(),
sku=new_form["sku"].value(),
category=new_form["category"].value(),
description=new_form["description"].value(),
)
new_product.save()
return redirect("products-services")
else:
print(new_form.errors)
context = {"formulario": new_form}
return render(request, "SkytechnosaApp/comercial/nuevoproducto.html", context)
def update_prod_serv(request, primarykey):
product_service = Catalogo.objects.get(id=primarykey)
item = product_service.item
sku = product_service.sku
category = product_service.category
description = product_service.description
form_data = {
"item": item,
"sku": sku,
"category": category,
"description": description,
}
form = NewProductForm(initial=form_data)
if request.method == "POST":
form = NewProductForm(request.POST, initial=form_data)
if form.is_valid():
form.save()
return redirect("products-services")
else:
print(form.errors)
context = {"form": form}
return render(request, "SkytechnosaApp/comercial/nuevoproducto.html", context)
The html works okay, the problem i'm facing is when i click on edit...it will populate the form with the information of the product or service i want to edit (that's fine), but then i make the changes on the comment field for example (just want to update comment) and then I get the error IntegrityError at /comercial/productos/nuevo
UNIQUE constraint failed: Comercial_catalogo.sku
It's like it's trying to create another product, because when i go back and i edit all the fields, and click on save, i see another product created, but I just wanted to update, rather than create a new product....what i am missing?
Thank you!
Your form code you pasted in your question is not complete (it's just a form.Form not a form.ModelForm, and yet you called form.save())
forms.py
class NewProductForm(forms.ModelForm):
item = forms.CharField(
widget=forms.TextInput(attrs={"class": "form-control"}),
label="Item",
max_length=100,
)
sku = forms.CharField(
widget=forms.TextInput(attrs={"class": "form-control"}),
label="Part number",
max_length=50,
)
category = forms.ChoiceField(
choices=categories,
label="Category",
)
description = forms.CharField(
widget=forms.Textarea(attrs={"class": "form-control"}),
label="Item description",
)
class Meta:
model = Catalogo
After that, if you want to update an instance (instead of creating one), you have to tell the ModelForm which instance it has to update:
view.py
def update_prod_serv(request, primarykey):
...
instance = Catalogo.objects.get(id=primarykey)
if request.method == 'POST':
form = NewProductForm(request.POST, instance=instance) # you don't need to use `initial` since
if form.is_valid():
form.save()
...
That said, it's generally best to have just ONE view for creating and updating. If you want to see how that works let me know.
I have got the code from https://github.com/adandan01/mybook, the code is working fine, even when I have updated it to Django 2. It's very simple project for adding a person in a form, and his/her relatives in the inline form. Everything works but when I add a relative name and forget to add his relationship, and submitted the form, unfortunately, that record will not pass the validation but will give no error messages as well. Django will ignore the entire record. For example, the record for Hawra in the image, will not be saved and Django will remove it. For this simple App there are only two fields to be filled (name and relationship), but I'm working on app with 8 fields, and it will be difficult to lose the data. is there any way to make django do the validation in the formset/subform as long as any fields have data and will ask the user to fill all required fields?
models.py:
class Profile(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
created_date = models.DateTimeField(default=timezone.now)
def get_absolute_url(self):
return reverse('profile-update', kwargs={'pk': self.pk})
def __unicode__(self):
return "%s %s" % (self.first_name, self.last_name)
class FamilyMember(models.Model):
profile = models.ForeignKey(Profile, on_delete=models.PROTECT)
name = models.CharField(max_length=100)
relationship = models.CharField(max_length=100)
form.py
class ProfileForm(ModelForm):
class Meta:
model = Profile
exclude = ()
class FamilyMemberForm(ModelForm):
class Meta:
model = FamilyMember
exclude = ()
FamilyMemberFormSet = inlineformset_factory(Profile, FamilyMember,
form=FamilyMemberForm, extra=1)
views.py
class ProfileCreate(CreateView):
model = Profile
fields = ['first_name', 'last_name']
class ProfileFamilyMemberCreate(CreateView):
model = Profile
fields = ['first_name', 'last_name']
success_url = reverse_lazy('profile-list')
def get_context_data(self, **kwargs):
data = super(ProfileFamilyMemberCreate, self).get_context_data(**kwargs)
if self.request.POST:
data['familymembers'] = FamilyMemberFormSet(self.request.POST)
else:
data['familymembers'] = FamilyMemberFormSet()
return data
def form_valid(self, form):
context = self.get_context_data()
familymembers = context['familymembers']
with transaction.atomic():
self.object = form.save()
if familymembers.is_valid():
familymembers.instance = self.object
familymembers.save()
return super(ProfileFamilyMemberCreate, self).form_valid(form)
I found the solution here django inline_formset - form.empty_permitted = False doesn't work
I had to add the following code before if (familymembers.is_valid():...) in the create and update class, so, now Django will show the error if I entered data in the Name field only and will tell me the Relationship field is required.
if familymembers.is_valid() == False:
return self.render_to_response(self.get_context_data(form=form,familymembers=familymembers ))
I created a django form (IssueForm) which is meant to be used to register an object which is instance of one of my models (Issue). Following are the model:
model.py
class Issue(models.Model):
TYPE_FIELDS = [
("Math", "Math"),
("Physics", "Physics"),
("Programming", "Programming"),
("Arts", "Arts")
]
issue_text = models.TextField(default="Please insert text")
issue_description = models.TextField(default="Newly created")
issue_deadline = models.DateField()
issue_field = models.CharField(max_length=30, choices=TYPE_FIELDS)
published_by = models.ForeignKey(User, on_delete=models.CASCADE, default=None)
def __str__(self):
return self.issue_description
the form used:
forms.py
class IssueForm(forms.ModelForm):
def __init__(self, user, *args, **kwargs):
self.user = user
super(IssueForm, self).__init__(*args, **kwargs)
TYPE_FIELDS = [
("Math", "Math"),
("Physics", "Physics"),
("Programming", "Programming"),
("Arts", "Arts")
]
issue_text = forms.CharField(widget=forms.Textarea, required=True)
issue_description = forms.CharField(widget=forms.Textarea, required=True)
issue_deadline = forms.DateField(required=True)
issue_fields = forms.ChoiceField(choices=TYPE_FIELDS, required=True)
class Meta:
model = Issue
fields = [
'issue_text',
'issue_description',
'issue_deadline',
'issue_fields'
]
def save(self, commit=True):
issue = super(IssueForm, self).save(commit=False)
issue.issue_text = self.cleaned_data['issue_text']
issue.issue_description = self.cleaned_data['issue_description']
issue.issue_deadline = self.cleaned_data['issue_deadline']
issue.issue_fields = self.cleaned_data['issue_fields']
if commit:
issue.published_by = self.user
issue.save()
return issue
and the related view:
views.py
def create_issue(request):
if ExtendedUser.objects.filter(user=request.user).exists():
if request.method == 'POST':
form = IssueForm(request.user, request.POST)
if form.is_valid():
form.save()
return redirect("/issues")
else:
form = IssueForm(request.user)
args = {'form': form}
return render(request, "issues/create_issue.html", args)
else:
raise Http404("You are not allowed to perform this action")
The forms works for every field in the model, they are all registered right, except for issue_fields. If i try giving a default value to the field in the model, that is the value that is saved on the database, otherwise I just get an empty field. Also I believe the problem comes from the form used, because if i try to create a new issue from the django admin interface it works just fine.
I feel like it's one of those silly mistakes, but I'm just starting with django and python in general and cannot figure it out on my own.
Thank you for your time!!
The field on your model is called issue_field, but you set issue_fields.
Note that also you are doing far more work here than necessary. Your save method completely duplicates what the superclass does already; you should remove all that code except for the setting of the user value.
enter code hereIf you want to use Choices, you haven't to write one more time list of choices in your forms.py file.
This is an example :
#In your models.py file
LIST_CHOICE = (('A','A'), ('B','B'))
class Test(models.Model) :
foo = models.CharField(choices=LIST_CHOICE, verbose_name="foo")
and
#In your form.py file
TestForm(forms.Modelform) :
class Meta :
model = Test
fields = ['foo']
It's not necessary to overwrite LIST_CHOICE in your form file ;)
So, dont touch to your model.py file, but in your form.py file, just write :
class IssueForm(forms.ModelForm):
issue_text = forms.CharField(widget=forms.Textarea)
issue_description = forms.CharField(widget=forms.Textarea)
def __init__(self, user, *args, **kwargs):
self.user = user
super(IssueForm, self).__init__(*args, **kwargs)
class Meta:
model = Issue
fields = [
'issue_text',
'issue_description',
'issue_deadline',
'issue_fields'
]
Don't forget to remove s in issue_field ;)
In my code CreateNoticeForm is working fine and all data gets saved perfectly except
many to many field which is tags in my notice model.Although it is working on admin site and gets saved.
here is my code
models.py
from django.db import models
from django.contrib.auth.models import User
from wysihtml5.fields import Wysihtml5TextField
# Create your models here.
class Tag(models.Model):
# For notice tags
name = models.CharField(max_length=70, unique=True)
def __str__(self):
return self.name
class Notice(models.Model):
# Notice Store
headline = models.CharField(max_length=140)
description = Wysihtml5TextField()
file_name = models.FileField(upload_to='static/noticefiles/', blank=True)
created_by = models.ForeignKey(User)
fors = models.CharField(max_length=1, choices=(('F','Faculty'),('S','Student'),) )
last_date = models.DateField()
tags = models.ManyToManyField(Tag, blank=True)
post_time = models.DateTimeField(auto_now_add=True)
update_time = models.DateTimeField(auto_now=True)
def __str__(self):
return self.headline
forms,py
FORS_CHOICES = (('F','Faculty'),('S','Student'))
class CreateNoticeForm(ModelForm):
fors = forms.ChoiceField(label="Related To",
choices=FORS_CHOICES,
)
class Meta:
model = Notice
fields = ('headline', 'description',
'fors', 'last_date', 'tags','file_name')
widgets = {
'description': Wysihtml5BootstrapWidget(),
'last_date': SelectDateWidget()
}
def __init__(self, *args, **kwargs):
super(CreateNoticeForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_id = 'create_notice_form_id'
self.helper.form_class = 'form-horizontal'
self.helper.form_method = 'post'
self.helper.label_class = 'col-lg-2'
self.helper.field_class = 'col-lg-8'
self.helper.layout = Layout(
Fieldset('Create Notice',
'headline',
'description',
Field('fors', label='Audience'),
MultiWidgetField('last_date',
attrs=(
{'style': 'width: 33.33%; display: inline-block;'}
)
),
'tags',
'file_name',
FormActions(
Submit('save', 'Create Notice',
css_class='btn-warning col-lg-offset-2'),
),
),
views.py
def create_notice(request):
context = RequestContext(request)
posted = False
if request.method=='POST':
create_notice_form = CreateNoticeForm(data=request.POST, files=request.FILES)
if create_notice_form.is_valid():
cnf = create_notice_form.save(commit=False)
cnf.created_by = request.user
cnf.save()
posted = True
else:
print(create_notice_form.errors)
else:
create_notice_form = CreateNoticeForm()
return render_to_response('notices/createnotice1.html',
{'create_notice_form': create_notice_form,
'posted': posted,},
context)
You have to call save_m2m():
cnf = create_notice_form.save(commit=False)
cnf.created_by = request.user
cnf.save()
create_notice_form.save_m2m()
Excerpt from the documentation:
If your model has a many-to-many relation and you specify commit=False when you save a form, Django cannot immediately save the form data for the many-to-many relation. This is because it isn’t possible to save many-to-many data for an instance until the instance exists in the database.
To work around this problem, every time you save a form using commit=False, Django adds a save_m2m() method to your ModelForm subclass. After you’ve manually saved the instance produced by the form, you can invoke save_m2m() to save the many-to-many form data.
You have to Call the following after validating the form:-
if create_notice_form.is_valid():
parent = create_notice_form.save(commit=False)
parent.save()
create_notice_form.save_m2m()
I hope this will help you
please help with the form is first loaded in the form fields display data from a database.
models:
class Status(models.Model):
status = models.CharField(max_length=40, blank=False, )
#classmethod
def get_status_list(self):
status_list = list()
status_list.append(('', 'no selected'))
for values_ins in Status.objects.all().values_list('id', 'status'):
status_list.append(values_ins)
return status_list
class UserProfile(User):
name1 = models.CharField('Имя', max_length=30, blank=True, null=True, )
name2 = models.CharField('Отчество', max_length=30, blank=True, null=True, )
status = models.ForeignKey(Status, verbose_name='Статус', blank=True, null=True, )
forms:
class PersonalDataForm(forms.ModelForm):
status = forms.ChoiceField(widget=forms.Select, choices=Status.get_status_list(),label='Статус',required=False, )
class Meta:
model = UserProfile
fields = ('name1', 'name2', )
views:
#login_required
def personal_data_page(request):
entry_user_profile = UserProfile.objects.get(user_ptr_id=request.user.id)
form = PersonalDataForm(instance=entry_user_profile)
if request.method == 'POST':
form = PersonalDataForm(request.POST, instance=entry_user_profile)
if form.is_valid():
form.save()
entry_user_profile.save()
return HttpResponseRedirect('/userprofile/personal_data_page_changed/')
t = loader.get_template('personal_data_page.html')
c = RequestContext(request, {'form': form,})
return HttpResponse(t.render(c))
template:
{{ form.name1 }}
{{ form.name2 }}
{{ form.status }}
the problem is that the form is first loaded the user can see the data from the database in the fields: "name1", "name2". but does not see the data from the database in the "status" (it shows a point "no selected", but must show the item corresponding to the value in the table UserProfile.status_id)
I need to when you first load the form in the "status" indicated a certain value. please help
You are using a model form but then you have override the status field in your form so you need to set the initial value manually by overriding the __init__ method (one way of doing it, it can be done in other ways also):
class PersonalDataForm(forms.ModelForm):
status = forms.ChoiceField(
widget=forms.Select,
choices=Status.get_status_list(),
label='Статус',
required=False
)
def __init__(self, *args, **kwargs):
super(PersonalDataForm, self).__init__(*args, **kwargs)
self.fields["status"].initial = self.instance.status.id # this line sets the value
class Meta:
model = UserProfile
fields = ('name1', 'name2', )
Following are some improvements for your other pieces of code.
Simplified get_status_list method:
#classmethod
def get_status_list(self):
status_list = Status.objects.all().values_list('id', 'status')
status_list.insert(0, ('', 'no selected'))
return status_list
You don't need to have entry_user_profile.save() after form.save(). The form save method will return the updated the profile instance by default. So following two lines:
form.save()
entry_user_profile.save()
Simplified as:
entry_user_profile = form.save()