I am quite new to Django. I am currently using ModelForm to save user input data to model. However, I would like to save data to other child models which inherits from parent model. However, my code saves data to the parent model. I guess that save() function should be overwritten to save data to child model, but I have no idea how to do it. Also, how should I scale my template or forms.py to include more forms with different submit button names(or any other method so that submitted forms could be distinguished and database populated accordingly)? Any insights highly appreciated. Please find my code below.
forms.py
from django import forms
from django.forms import modelformset_factory, ModelForm
from .models import Assumptions, Scenario1_Assumptions,
Scenario2_Assumptions, Scenario3_Assumptions, Scenario4_Assumptions ...
Scenario_N_Assumptions
class AssumptionsForm(ModelForm):
class Meta:
model = Assumptions
fields = ['Worst_Case', 'Grey_Case', 'Red_Case', 'Blue_Case',
'Green_Case', 'Best_Case']
exclude = ()
AssumptionsFormSet = modelformset_factory(Assumptions, form=AssumptionsForm,
extra = 5)
views.py
from django.shortcuts import render
from .forms import AssumptionsFormSet, modelformset_factory
from .models import Scenario1_Assumptions, Assumptions
def get_assumptions(request):
if request.method == 'POST':
formset = AssumptionsFormSet(request.POST)
if formset.is_valid():
if 'scenario1' in request.POST:
for form in formset:
Scenario1_Assumptions = form.save() #THIS DOESN'T WORK
if 'scenario2' in request.POST:
for form in formset:
Scenario2_Assumptions = form.save() #and so on...
else:
formset = AssumptionsFormSet()
return render(request, 'assumptions.html', {'formset': formset})
models.py
from django.db import models
from django.forms import ModelForm
class Assumptions(models.Model):
Worst_Case = models.FloatField(null=True, blank=True, default=None)
Grey_Case = models.FloatField(null=True, blank=True, default=None)
Red_Case = models.FloatField(null=True, blank=True, default=None)
Blue_Case = models.FloatField(null=True, blank=True, default=None)
Green_Case = models.FloatField(null=True, blank=True, default=None)
Best_Case = models.FloatField(null=True, blank=True, default=None)
class Scenario1_Assumptions(Assumptions):
pass
class Scenario2_Assumptions(Assumptions):
pass
class Scenario3_Assumptions(Assumptions):
pass
class Scenario4_Assumptions(Assumptions):
pass
.
.
.
class Scenario_N_Assumptions(Assumptions):
pass
Assumptions.html
<form action="" method="post">
{% csrf_token %}
{{ formset.management_form }}
{{ formset.non_form_errors.as_ul }}
<table id="formset" class="form">
{% for form in formset.forms %}
{% if forloop.first %}
<thead><tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
</tr></thead>
{% endif %}
<tr class="{% cycle 'row1' 'row2' %}">
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<input type="submit", name = "scenario1", value="Submit" />
</form>
Related
Whenever I click on the profiles(links) on the profile_list page, I get the profile doesn't exist error
it always goes to profiles that do not longer exist in the database.
Models.py:
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
# Create your models here.
class Profile(models.Model):
user=models.OneToOneField(User,on_delete=models.CASCADE)
follows=models.ManyToManyField(
"self",
related_name="followed_by",
symmetrical=False,
blank=True)
def __str__(self):
return self.user.username
#receiver(post_save,sender=User)
def create_profile(sender, instance, created, **kwargs):
if created:
user_profile=Profile(user=instance)
user_profile.save()
user_profile.follows.add(instance.profile)
user_profile.save()
admin.py:
from django.contrib import admin
from django.contrib.auth.models import Group,User
from .models import Profile
class ProfileInline(admin.StackedInline):
model=Profile
class UserAdmin(admin.ModelAdmin):
model=User
fields=["username"]
inlines=[ProfileInline]
# Register your models here.
admin.site.unregister(Group)
admin.site.unregister(User)
admin.site.register(User,UserAdmin)
views.py:
from django.urls import reverse
from django.shortcuts import render
from requests import request
from users.models import Profile
from django.http import HttpResponseRedirect
# Create your views here.
def dashboard(request):
return render(request,"inbox/layout.html")
def feed(request):
return render(request,"inbox/feed.html")
def outbox(request):
return render(request,"inbox/outbox.html")
def profile_list(request):
profiles=Profile.objects.exclude(user=request.user)
return render(request,"inbox/profile_list.html",{"profiles":profiles})
def profile(request, pk):
if not hasattr(request.user, 'profile'):
missing_profile = Profile(user=request.user)
missing_profile.save()
profile = Profile.objects.get(pk=pk)
if request.method == "POST":
current_user_profile = request.user.profile
data = request.POST
action = data.get("follow")
if action == "follow":
current_user_profile.follows.add(profile)
elif action == "unfollow":
current_user_profile.follows.remove(profile)
current_user_profile.save()
return render(request, "inbox/profile.html", {"profile": profile})
profile.html:
<!-- inbox/templates/inbox/profile_list.html -->
{% extends 'inbox/layout.html' %}
{% block content %}
<form method="post">
{% csrf_token %}
<div >
<p>#{{ profile.user.username|upper }}</p>
<div class="buttons has-addons">
{% if profile in user.profile.follows.all %}
<button class="button is-success is-static">Follow</button>
<button class="button is-danger" name="follow" value="unfollow">
Unfollow
</button>
{% else %}
<button class="button is-success" name="follow" value="follow">
Follow
</button>
<button class="button is-danger is-static">Unfollow</button>
{% endif %}
</div>
<p1>following:</p1>
<ul>
{% for following in profile.follows.all %}
<li>{{ following }}</li>
{% endfor %}
</ul>
<p2>followers:</p2>
<ul>
{% for follower in profile.followed_by.all %}
<li>{{ follower }}</li>
{% endfor %}
</ul>
</div>
</form>
{% endblock content %}
profile_list.html template:
<!-- inbox/templates/inbox/profile_list.html -->
{% extends 'inbox/layout.html' %}
{% block content %}
<form method="post">
{% csrf_token %}
{% for profile in profiles %}
<div class="column">
<p>{{ profile.user.username }}</p>
<p>#{{ profile.user.username|lower }}</p>
</div>
{% endfor %}
</form>
{% endblock content %}
You need to match Profile's pk with profile = Profile.objects.get(pk=pk), currently you mentioned the pk of User model, which matched the given queryset in profile view, so try sending pk of profile in profile_list.html template:
{% for profile in profiles %}
<div class="column">
<p>{{ profile.user.username }}</p>
<p>#{{ profile.user.username|lower }}</p>
</div>
{% endfor %}
Also, it is better to use get_object_or_404 instead of get, as it calls get() on a given model manager, but it raises Http404 instead of the model’s DoesNotExist exception.
So, views.py:
def profile(request, pk):
if not hasattr(request.user, 'profile'):
missing_profile = Profile(user=request.user)
missing_profile.save()
profile = get_object_or_404(Profile, pk=pk)
if request.method == "POST":
current_user_profile = request.user.profile
data = request.POST
action = data.get("follow")
if action == "follow":
current_user_profile.follows.add(profile)
elif action == "unfollow":
current_user_profile.follows.remove(profile)
current_user_profile.save()
return render(request, "home/profile.html", {"profile": profile})
Now, if the profile does not exist, it will show no Profile matches the given query.
I have a project where users can create a company. However, there is a Theme that needs to be dynamically inserted, once the user chooses a theme on the frontend - more like when you want to choose a template in a no-code builder. Below is my models.py
class Theme(models.Model):
name = models.CharField(max_length=100, null=True)
image = models.ImageField(upload_to='static/images/themes_images', null=True, blank=True)
category = models.CharField(max_length=200, choices=CATEGORY_CHOICES, null=True)
class Company (models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, editable=False)
date_created = models.DateField(default=timezone.now, null=True)
company_name = models.CharField(max_length=100, null=True)
theme = models.ForeignKey(Theme, on_delete=models.CASCADE, null=True, blank=True)
For the Theme model, it is only the administrator who will create it and the user will only have to choose it on the front end. Sort of like when choosing a premade template in Weebly. All the Theme Objects will have their own page where the user can visually see which theme to go with:
#login_required(login_url='login')
def theme(request):
themes = Theme.objects.all()
context = {'themes': themes}
return render(request, 'themes.html', context)
I haven't done much with the view, cause I am not sure how to go about it to achieve the desired result.
Themes.html
{% extends 'index.html' %}
{% load static %}
{% block 'title' %} <title>Themes | Simpledocs</title> {% endblock %}
{% block content2 %}
{% endblock %}
{% block content %}
<h2 class="my-4">Choose your theme</h2>
<div class="columns">
{% for t in themes %}
<div class="column is-4">
{{t.name}}<br>
<img src = "/{{t.image}}" height="150px" width="150"/><be>
{{t.category}}<br>
<a class="button" href="#">Choose Theme</a>
</div>
{% endfor %}
</div>
{% endblock %}
So with a click on the above view, the user should be able to select a theme and apply it dynamically in the Company model on the theme entry. I have never dealt with such a challenge and need help here.
It is actually easy. Got the solution.
Create another view that shows individual themes:
{% extends 'index.html' %}
{% load static %}
{% block 'title' %} <title>Themes | Simpledocs</title> {% endblock %}
{% block content2 %}
{% endblock %}
{% block content %}
<h2 class="my-4">Choose your theme</h2>
<div class="columns">
{{single_theme.name}}<br><br>
<img src="/{{single_theme.image}}" /><br><be>
{{single_theme.category}}<br>
<form method="POST">
{% csrf_token %}
<button class="button">Choose Theme</button>
</form>
</div>
{% endblock %}
Create another View showcasing the theme. And execute the change of the Company.theme entry.
#login_required(login_url='login')
def theme_single(request, pk):
single_theme = Theme.objects.get(id=pk)
company = Company.objects.all()
if request.method == 'POST':
for c in company:
c.theme = single_theme
c.save()
return redirect('company')
context = {'single_theme':single_theme}
return render(request, 'theme_single.html', context)
This will change the company.theme model entry to the current theme selected.
I am following a Django tutorial on Youtube, I added a bio field in the UserUpdateForm. There is a slot for me to edit the bio on change_profile.html but when I press the update button it updates everything else except for the bio.
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from .models import Profile
class UserRegisterForm(UserCreationForm):
email = forms.EmailField()
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
class UserUpdateForm(forms.ModelForm):
email = forms.EmailField()
# What I added
bio = forms.CharField(required=False)
class Meta:
model = User
fields = ['username', 'email', 'bio']
class ProfileUpdateForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['image']
The function that saves the forms
#login_required
def change_profile(request):
if request.method == 'POST':
u_form = UserUpdateForm(request.POST, instance=request.user)
p_form = ProfileUpdateForm(request.POST, request.FILES, instance=request.user.profile)
if u_form.is_valid() and p_form.is_valid():
u_form.save()
p_form.save()
messages.success(request, 'Profile Updated')
return redirect('profile')
else:
u_form = UserUpdateForm(instance=request.user)
p_form = ProfileUpdateForm(instance=request.user.profile)
context = {
'u_form' : u_form,
'p_form' : p_form
}
return render(request, 'users/change_profile.html', context)
The change_profile.html
{% extends "blog/base.html" %}
{% load crispy_forms_tags %}
{% block title %}Change Profile{% endblock title %}
{% block content %}
<div class="content-section">
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Edit Profile</legend>
{{ u_form|crispy }}
{{ p_form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Update</button>
</div>
</form>
</div>
{% endblock content %}
And the profile.html
{% extends "blog/base.html" %}
{% block title %}Profile{% endblock title %}
{% block content %}
<div class="content-section">
<div class="media">
<img class="rounded-circle account-img" src="{{ user.profile.image.url }}">
<div class="media-body">
<h2 class="account-heading">{{ user.username }}</h2>
<p class="text-secondary">{{ user.email }}</p>
<p class="article-content">{{ user.bio }}</p>
</div>
</div>
<a class="ml-2" href="{% url 'change_profile' %}">Edit Profile</a>
{% endblock content %}
It's because the default User model has no attribute called bio, so there's nowhere to store the value you're getting from the form. You need to add it to the model first. You can create a custom user model, but since you already have a Profile model, you can store bio along with image:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField()
bio = models.CharField(max_length=225, blank=True, null=True)
And in forms.py add the new field to the field list:
class ProfileUpdateForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('image', 'bio')
You could have simply override the the User model and add your custom fields, then you don't need to add extra fields in your form. Check this example:
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User)
bio = models.TextField()
def __str__(self):
return unicode(self.user)
Make sure you add mention your custom User model in settings:
AUTH_USER_MODEL ='your_app.UserProfile'
I was debugging my program and I noticed that on the line Formset.is_valid i am getting error:
missing 1 required positional argument: 'self'
Could you please advise how it could be solved to make formset and all the forms to validate successfully and save to database. Also, is double validation (formset and form) necessary? Thank you for insights.
views.py
from django.shortcuts import render
from .forms import modelformset_factory, AssumptionsForm
from .models import Assumptions
import pdb
model_names = ['Form1', 'Form2']
def get_assumptions(request):
if request.method == 'POST':
print('Reached post')
formset = modelformset_factory(
Assumptions, form=AssumptionsForm, extra=5)
pdb.set_trace()
for thing in model_names:
if thing in request.POST:
print('template name in model_names')
if formset.is_valid():
for form in formset:
if form.is_valid():
print('in for loop after valid form1')
assumptions = form.save(commit='False')
assumptions.Name = thing
assumptions.save()
else:
formset = modelformset_factory(
Assumptions, form=AssumptionsForm, extra=5)
print('reached else')
return render(request, 'assumptions.html', {'formset': formset, 'model_names': model_names})
models.py
from django.db import models
from django.forms import ModelForm
class Assumptions(models.Model):
Worst_Case = models.FloatField(null=True, blank=True, default=None)
Grey_Case = models.FloatField(null=True, blank=True, default=None)
Red_Case = models.FloatField(null=True, blank=True, default=None)
Blue_Case = models.FloatField(null=True, blank=True, default=None)
Green_Case = models.FloatField(null=True, blank=True, default=None)
Best_Case = models.FloatField(null=True, blank=True, default=None)
Name = models.TextField(null=True, blank=True, default=None)
assumptions.html
<div class="form">
<form action="" method="post">
{% csrf_token %}
{{ formset.management_form }}
{{ formset.non_form_errors.as_ul }}
{% for vardas in model_names %}
<h1>{{vardas}}</h1>
<table id="formset" class="form">
{% for form in formset.forms %}
{% if forloop.first %}
<thead><tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
</tr></thead>
{% endif %}
<tr class="{% cycle 'row1' 'row2' %}">
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<input type="hidden" name="{{vardas}}" />
{% endfor %}
<input type="submit" value="Next">
</form>
</div>
forms.py
from django import forms
from django.forms import modelformset_factory, ModelForm
from .models import Assumptions
class AssumptionsForm(ModelForm):
class Meta:
model = Assumptions
fields = ['Worst_Case', 'Grey_Case', 'Red_Case', 'Blue_Case', 'Green_Case', 'Best_Case']
exclude = ['Name']
You haven't instantiated your formset. A factory is something that returns a class, so here modelformset_factory returns a formset class that you then need to instantiate in both your get and post blocks.
def get_assumptions(request):
AssumptionsFormset = modelformset_factory(
Assumptions, form=AssumptionsForm, extra=5)
if request.method == 'POST':
formset = AssumptionsFormset(request.POST)
if formset.is_valid():
...
else:
formset = AssumptionsFormset()
return render(request, 'assumptions.html', {'formset': formset, 'model_names': model_names})
Also note, you don't need to check is_valid() on each form; if the formset is valid, all the forms will be valid.
The issue is that Django modelformset_factory does not save to database when inputs are inserted into the forms. It appears that only else statement is reached after forms are submitted for processing. Clearly the issue is with part in views.py where if 'name' in request.POST: then do sth. Advise how to solve it would be highly appreciated. Thank you.
views.py
from django.shortcuts import render
from .forms import modelformset_factory, AssumptionsForm
from .models import Assumptions
model_names = ['Form1', 'Form2']
def get_assumptions(request):
if request.method == 'POST' and 'name' in request.POST:
formset = modelformset_factory(
Assumptions, form=AssumptionsForm, extra=5)
if formset.is_valid():
print('valid form')
for form in formset:
if form.is_valid():
print('in for loop after valid form1')
assumptions = form.save(commit='False')
assumptions.Name = 'name'
assumptions.save()
else:
formset = modelformset_factory(
Assumptions, form=AssumptionsForm, extra=5)
print('reached else')
return render(request, 'assumptions.html', {'formset': formset, 'model_names': model_names})
models.py
from django.db import models
from django.forms import ModelForm
class Assumptions(models.Model):
Worst_Case = models.FloatField(null=True, blank=True, default=None)
Grey_Case = models.FloatField(null=True, blank=True, default=None)
Red_Case = models.FloatField(null=True, blank=True, default=None)
Blue_Case = models.FloatField(null=True, blank=True, default=None)
Green_Case = models.FloatField(null=True, blank=True, default=None)
Best_Case = models.FloatField(null=True, blank=True, default=None)
Name = models.TextField(null=True, blank=True, default=None)
forms.py
from django import forms
from django.forms import modelformset_factory, ModelForm
from .models import Assumptions
class AssumptionsForm(ModelForm):
class Meta:
model = Assumptions
fields = ['Worst_Case', 'Grey_Case', 'Red_Case', 'Blue_Case', 'Green_Case', 'Best_Case']
exclude = ['Name']
assumptions.html
<div class="form">
<form action="" method="post">
{% csrf_token %}
{{ formset.management_form }}
{{ formset.non_form_errors.as_ul }}
{% for name in model_names %}
<h1>{{name}}</h1>
<table id="formset" class="form">
{% for form in formset.forms %}
{% if forloop.first %}
<thead><tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
</tr></thead>
{% endif %}
<tr class="{% cycle 'row1' 'row2' %}">
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<input type="submit" name="{{name}}" value="save" />
{% endfor %}
</form>
</div>
name needs to be in your request.POST,
doublecheck your template and add an input tag with a name='name'
<input name='name' />