Django DRF how to 'multiple object update'? - python

I am trying to update multiple objects, however i am stumped on what i am doing wrong. I get queryset of objects i need to update, by filtering by schoolName(this is how my DB looks like), but I get "'ListSerializer' object is not iterable" when I include {% render_form serializer %} in my template.
I have uploaded my project to github
Relevant code:
models.py:
class School(models.Model):
schoolName = models.CharField(max_length=200, default='No Data')
className = models.CharField(max_length=200, default='No Data')
studentName = models.CharField(max_length=200, default='No Data')
studentGrade = models.IntegerField(null=True, blank=True)
professorName = models.CharField(max_length=200, default='No Data')
def __str__(self):
return self.schoolName
serializers.py:
class SchoolPartListSerializer(serializers.ListSerializer):
def update(self, instance, validated_data):
# Maps for id->instance and id->data item.
school_mapping = {school.id: school for school in instance}
data_mapping= {schoolName['id']: schoolName for schoolName in validated_data}
# Perform creations and updates.
ret = []
for school_id, data in data_mapping.schoolName():
school = school_mapping.get(school_id, None)
if school is None:
ret.append(self.child.create(data))
else:
ret.append(self.child.update(school, data))
# Perform deletions.
for school_id, school in school_mapping.schoolName():
if school_id not in data_mapping:
school.delete()
return ret
class SchoolPartSerializer(serializers.Serializer):
id = serializers.IntegerField()
class Meta:
list_serializer_class = SchoolPartListSerializer
model = School
fields = ('id', 'url', 'schoolName')
extra_kwargs = {
'id':{
'read_only': False,
'allow_null': True,
},
'schoolName':{
'required': True,
}
}
views.py:
class SchoolDetail(APIView):
renderer_classes = [TemplateHTMLRenderer]
template_name = 'tmp_school_detail.html'
def get(self, request, name, *args, **kwargs):
school = School.objects.filter(schoolName=name)
serializer_context = {'request': request, }
serializer = SchoolPartSerializer(instance=school, many=True, context=serializer_context)
return Response({'serializer': serializer, 'school': school})
def post(self, request, name):
school = School.objects.filter(schoolName=name)
#school = get_object_or_404(School.objects.only('id', 'schoolName'))
serializer_context = {'request': request,}
serializer = SchoolPartSerializer(instance=school, many=True, data=request.data, context=serializer_context)
if not serializer.is_valid():
return Response({'serializer': serializer, 'school': school})
serializer.save()
return redirect('tmp_school-list')
urls.py:
path(r'tmp_school_list/', views.SchoolList.as_view(), name='tmp_school-list'),
path(r'tmp_school_detail/<str:name>/', views.SchoolDetail.as_view(), name='tmp_school-detail'),
tmp_school_list.html:
{% load rest_framework %}
<html><body>
<h1>School - {{ school.schoolName }}</h1>
<form action="{% url 'tmp_school-detail' name=school.first.schoolName %}" method="POST">
{% csrf_token %}
{% render_form serializer %}
<input type="submit" value="Save">
</form>
Back
</body></html>
I have tried following documentation and have looked for other solutions(this one the best one i found), but nothing worked for me.
The goal is to update all data enteries that has a specificl schoolName.

Related

Why is the form not shown on the website?

I have a feedback form and it should be located on the main page along with other models. But alas, for some reason it refers to a non-existent template. And thus it is not shown on the site, although the model itself is present in the admin panel.
Please help me figure it out.
I was trying many ways to figure out it, but theres nothing
views.py
class MainDetail(CreateView):
success_url = reverse_lazy('success_page')
form_class = ContactForm
def get(self, request, **kwargs):
search_query = request.GET.get('search', )
model_2 = Jobs.objects.order_by()
form = ContactForm(request.POST)
portfolio = Portfolio.objects.prefetch_related('image')
ctx = {
'jobs':model_2,
'portfolio': portfolio
}
form = ContactForm(request.POST)
if form.is_valid():
form.save()
return render(request, 'homepage.html', ctx)
def form_valid(self, form):
# Формируем сообщение для отправки
data = form.data
subject = f'Сообщение от {data["first_name"]} {data["last_name"]},Тема: ORDER {data["phone"]}'
email(subject, data['message'])
return super().form_valid(form)
model.py
class Contact(models.Model):
first_name = models.CharField ('Name', max_length=25)
last_name = models.CharField('Last Name', max_length=30)
phone = models.EmailField ('Phone', max_length=15)
message = models.TextField (max_length=400)
class Meta:
verbose_name= 'Feedback'
verbose_name_plural = 'Feedsbacks'
def __str__(self):
class Meta:
verbose_name= 'Feedback'
verbose_name_plural = 'Feedsbacks'
def __str__(self):
return self.first_name
forms.py
class ContactForm(ModelForm):
class Meta:
model = Contact
fields = ['first_name', 'last_name', 'phone', 'message']
widgets = {
'first_name': forms.TextInput (
attrs={'placeholder':'Name'}
),
'last_name': forms.TextInput(
attrs={'placeholder': 'Wash'}
),
'phone': forms.TextInput(
attrs={'placeholder': '+7 (123) 45-67-89'}
),
'message': Textarea(
attrs={
'placeholder': 'Message',
"rows":6,
"cols": 25
}
)
}
html
<aside class="postcard">
<form class="form" method="POST">
<p>Order</p>
{% csrf_token %}
{{ form.as_p }}
<div class="form-group">
<input type="submit" value="Send">
</div>
</form>
</aside>
Thanks for any help
Here it is the solution to all the troubles
class MainDetail(CreateView):
model = Jobs
model_2 = Portfolio
model_3 = Contact
template_name = 'homepage.html'
success_url = reverse_lazy('success_page')
form_class = ContactForm
def get_context_data(self, **kwargs):
context = super(MainDetail, self).get_context_data(**kwargs)
context['first_form'] = ContactForm (self.request.POST)
context['jobs'] = Jobs.objects.order_by()
context['portfolio'] = Portfolio.objects.prefetch_related('image')
return context

Django UpdateView with three different modelforms

I have a django UpdateView which needs to inherit three different models and different models. CreateView is working fine with three different modelforms.
models.py:
class Employee(models.Model):
"""
Create employee attributes
"""
employee_user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True)
e_id = models.IntegerField(unique=True, null=True, blank=True)
first_name = models.CharField(max_length=128,blank=True)
last_name = models.CharField(max_length=128, blank=True)
.....
class WorkExperience(models.Model):
"""
Stores employee previous work experiences
"""
employee = models.ForeignKey('Employee', related_name='we_employee', on_delete=models.CASCADE, null=True)
previous_company_name = models.CharField(max_length=128, null=True)
job_designation = models.CharField(max_length=128, null=True)
from_date = models.DateField(null=True)
......
class Education(models.Model):
"""
Stores employee education background
"""
employee = models.ForeignKey('Employee', related_name='edu_employee', on_delete=models.CASCADE, null=True)
institution_name = models.CharField(max_length=128, null=True)
degree = models.CharField(max_length=128, null=True)
.....
views.py:
class EmployeeUpdateView(LoginRequiredMixin,UpdateView):
"""
Update a created a employee
"""
login_url = '/authentication/login/'
template_name = 'employee/employee_add_form.html'
form_class = EmployeeAddModelForm
work_form_class = WorkExperienceForm
education_form_class = EducationForm
queryset = Employee.objects.all()
def form_valid(self, form):
print(form.cleaned_data)
return super().form_valid(form)
def get_object(self):
id_ = self.kwargs.get("id")
return get_object_or_404(Employee, id=id_)
WHen I go to Update view, I am only able to update EmployeeAddModelForm values. But other form's(WorkExperienceForm, EducationForm ) fields do not appear let alone edit the values of the fields.
I suppose my views.py is not correct.
I need suggestion to correct my updateview.
If you want to put multiple forms in one HTML file, in my way, write as follows.
class EmployeeUpdateView(LoginRequiredMixin,UpdateView):
login_url = '/authentication/login/'
def form_valid(self, form):
print(form.cleaned_data)
return super().form_valid(form)
def get(self, request, *args, **kwargs):
id_ = self.kwargs.get("id")
user = Employee.objects.get(id=id_)
work = WorkExperience.objects.get(employee=user)
education = Education.objects.get(employee=user)
return render(request, self.template_name, {
'form': EmployeeAddModelForm(instance=user),
'work_form': WorkExperienceForm(instance=work),
'education_form': EducationForm(instance=education)
}
)
def post(self, request):
first_name = request.POST['first_name']
# Your own code...
.....
I think the argument of each form depends on the definition of Form.
And in a very simple way, the HTML file looks like this:
<body>
<form method="post">
{% csrf_token %}
{{ employ_form }}
{{ work_form }}
{{ edu_form }}
<button type="submit">OK</button>
</form?
</body>
</html>
I'm not sure if this is the best option.
How about putting forms into get_context_data?
Something like
context['form1'] = EmployeeAddModelForm()
context['form2] = WorkExperienceForm()
Etc.
Edit:
def get_context_data(self, **kwargs):
context = super(EmployeeUpdateView, self).get_context_data(**kwargs)
context['form'] = EmployeeAddModelForm()
context['form2'] = WorkExperienceForm()
context['form3'] = EducationForm()
return context

How can I fill a field with current user in form using CreateView/UpdateView in Django?

I've been looking for this for few days and I couldn't solve it, so here is my problem:
I'm trying to create a product using just CreateView in views.py for now (My idea is to create forms and pass everything to a function in views.py), but there is one field that I want it to be auto-filled with the logged user ('owner'). I've tried using the get_initial in the createView for now, but it doesn't work.
I want to say that this it actually creates a form in which I have to fill all the fields and it works fine, but I want to auto-fill the 'owner' field with the current user logged in.
For now I tried to use the get_initial as I said before, but seems that it does not work. I also tried lots of things that I've seen here, in stackoverflow, but any of them worked for me.
Here I put all the relevant code, but if you need anything else please, ask for it and I'll upload it.
This is my views.py:
# view for the product entry page
class ProductCreate(CreateView):
model = Product
fields = ['owner', 'category', 'tag', 'name', 'content_tweet']
success_url = reverse_lazy('index')
def get_form(self, form_class=None):
form = super(ProductCreate, self).get_form(form_class)
return form
def get_initial(self):
return {
'owner': self.request.user,
}
def dispatch(self, request, *args, **kwargs):
if not request.user.is_superuser or request.user.is_vendor:
raise PermissionDenied()
return super().dispatch(request, *args, **kwargs)
This is the model I'm using in models.py:
class Product(models.Model):
owner = models.ForeignKey(CustomUser, on_delete=models.PROTECT)
name = models.CharField(_('Product Name'), max_length=150)
category = models.ManyToManyField(Category)
tag = models.ManyToManyField(Tag)
content_tweet = models.CharField(_('Content Tweet'), max_length=150, blank=True)
content_abstract = models.CharField(_('Content Abstract'), max_length=3000, blank=True)
canvas_1 = models.FileField(upload_to='documents/canvas_1/', blank=True)
price_tweet = models.IntegerField(_('Tweet Price in Tokens'), default=0)
price_abstract = models.IntegerField(_('Abstract Price in Tokens'), default=50)
price_canvas_1 = models.IntegerField(_('Canvas 1 Price in Tokens'), default=500)
it_exist = models.BooleanField(_('is an existing idea?'), default=False)
is_active = models.BooleanField(
_('active'),
default=True,
help_text=_(
'Designates whether this product should be treated as active. '
'Unselect this instead of deleting products.'
),
)
history = HistoricalRecords()
class Meta:
unique_together = ('name', 'owner')
def get_categories(self):
return ",".join([str(p) for p in self.category.all()])
def get_tags(self):
return ",".join([str(p) for p in self.tag.all()])
def __str__(self):
return self.name
And this is the template I'm using (for now) in my XX.html:
{% for field in form %}
<div class="form-group">
<div >
<span class="text-danger la">{{ field.errors }}</span>
</div>
<label >{{ field.label_tag }}</label>
<div class="col-sm-10">{{ field }}</div>
</div>
{% endfor %}
def form_valid(self, form):
form.instance.owner = self.request.user
return super(ProductCreate, self).form_valid(form)
override form_valid method of CreateView as above

How do I prepopulate a form with values from a database in Django?

I'm writing what should be a very simple todo app. The problem is that the edit view is giving me fits! I'm trying to populate a form with data from the database, and it's just not doing the right thing. I've tried the info from this page, but the translation into class-based views must have broken something, or I'm just not using the right kind of form.
Here's the code for the model:
class Todo(models.Model):
id = models.AutoField(primary_key=True)
todo = models.CharField(max_length=255, unique=True)
todo_detail = models.TextField(default='')
date_created = models.DateField(default=timezone.now())
estimated_completion = models.DateTimeField(default=timezone.now())
maybe_completed = models.BooleanField("Completed?", default=False)
def __unicode__(self):
return self.todo
The view code, the commented out bit is from the link:
class TodoEditView(FormView):
model = Todo
form_class = TodoEditForm
template_name = 'todo_edit.html'
#def get(self, request, *args, **kwargs):
# form = self.form_class()
# form.fields['todo'].queryset = Todo.objects.get(id=self.kwargs['pk'])
# form.fields['todo_detail'].queryset = Todo.objects.get(
# id=self.kwargs['pk'])
# form.fields['date_created'].queryset = Todo.objects.get(
# id=self.kwargs['pk'])
# form.fields['estimated_completion'].queryset = Todo.objects.get(
# id=self.kwargs['pk'])
# form.fields['maybe_completed'].queryset = Todo.objects.get(
# id=self.kwargs['pk'])
# template_vars = RequestContext(request, {
# 'form': form
# })
# return render_to_response(self.template_name, template_vars)
def get_context_data(self, **kwargs):
context = super(TodoEditView, self).get_context_data(**kwargs)
context['todo'] = Todo.objects.get(id=self.kwargs['pk'])
return context
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
todo = request.POST['todo']
todo_detail = request.POST['todo_detail']
estimated_completion = request.POST['estimated_completion']
date_created = request.POST['date_created']
t = Todo(todo=todo, todo_detail=todo_detail,
estimated_completion=estimated_completion,
date_created=date_created)
t.save()
return redirect('home')
The form code:
class TodoEditForm(forms.ModelForm):
class Meta:
model = Todo
exclude = ('id', )
And the template code:
{% extends 'todos.html'%}
{% block content %}
<form method="post" action="{% url 'add' %}">
<ul>
{{ form.as_ul }}
{% csrf_token %}
</ul>
{{todo.todo}}
</form>
{% endblock %}
What the heck am I doing wrong?
You should use an UpdateView, not a FormView. That will take care of prepopulating your form.
Also note you don't need any of the logic in the post method - that is all taken care of by the generic view class.

Using formset with ContentType

I'm having trouble using the UpdateView for a view consisting of a form and formset.
I have the following models: Item and Picture.
Picture is defined as:
class Picture(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255, blank=False)
content_type = models.ForeignKey(ContentType, verbose_name="content type",
related_name="content_type_set_for_%(class)s")
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey("content_type", "object_id")
I have several models that contain pictures. For example, in the Item model:
class Item(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255, blank=False)
pictures = generic.GenericRelation(Picture)
I have the following ItemCreateForm:
class ItemCreateForm(ModelForm):
def __init__(self, *args, **kwargs):
super(ItemCreateForm, self).__init__(*args, **kwargs)
class Meta:
model = Item
The PictureForm:
class PictureForm(forms.ModelForm):
id = forms.IntegerField(widget=forms.HiddenInput)
def __init__(self, *args, **kwargs):
super(PictureForm, self).__init__(*args, **kwargs)
def save(self):
data = self.cleaned_data
obj = Picture(**data);
# do something to obj
# obj.save()
class Meta:
model = Picture
fields = ['id', 'name']
And the view:
class ItemUpdateView(UpdateView):
form_class = ItemCreateForm
template_name = 'item/new.html'
model = Item
success_url = '/items/'
def get_context_data(self, **kwargs):
context = super(ItemUpdateView, self).get_context_data(**kwargs)
item = context['object']
# Dont' create any extra forms when showing an update view
PictureFormSet = formset_factory(PictureForm, extra=0)
return {'form': kwargs['form'],
'picture_formset': UploadFormSet(initial = [ model_to_dict(a) for pic in item.pictures.all()])}
def post(self, request, *args, **kwargs):
self.object = self.get_object()
item_form = ItemCreateForm(request.POST, instance=self.object)
if item_form.is_valid():
item = item_form.save(commit=False)
item.save()
# How do update the pictures?
This is my urls.py:
url(r'^items/(?P<pk>\d+)/update/$', ItemUpdateView.as_view(), name='item_update')
The template:
<form action="" method="post" enctype="multipart/form-data">
{% for field in form %}
# do something
{% endfor %}
{{ picture_formset.management_form }}
{% for form in picture_formset.forms %}
# do something
{% endfor %}
<input name="commit" type="submit" value="Submit" />
</form>
I'm new to Django.
The user can dynamically(via jQuery) add/remove pictures through the Picture form in the single template that is used to display the item and multiple pictures.
1 I had to include the id as a hidden field for the picture, otherwise the pictures will be inserted instead of an Update. QN: Is there a better way to do this?
2 How do I update the picture model? Currently request.POST doesn't have all the fields in the model, thus the model is complaining of NULL fields? I'm totally at lost how to deal with formset in an UpdateView and is not the main form, like a simple example of UpdateView with the pk in the url.
PictureFormSet = formset_factory(PictureForm)
picture_formset = PictureFormSet(request.POST, request.FILES)
for picture_form in picture_formset.forms:
picture_form.save()

Categories

Resources