I have a Django model class that extends the django.contrib.auth.models.User:
class MyModel(models.Model):
user = models.OneToOneField(User, models.CASCADE, unique=True)
bio = models.TextField()
date_of_birth = models.DateField()
and out of this model I'm making a ModelForm:
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ['bio', 'date_of_birth']
How can I tell the MyModelForm to take in the User's fields such as username, first_name, last_name?
Example in the class Meta fields I can add ['bio', 'date_of_birth', 'user'] but that will just provide me with the dropdown to select to whom shall the MyModel in the MyModelForm be related to.
Doing ['bio', 'date_of_birth', 'user.first_name'] throws an exception django.core.exceptions.FieldError: Unknown field(s) (user.first_name) specified for MyModel
Edit: here's the traceback
https://pastebin.com/0T42VKEP
I kinda found my own solution, by overriding ModelForm's __init__ method:
class MyModelForm(forms.ModelForm):
first_name = forms.CharField()
class Meta:
model = MyModel
fields = ['bio', 'date_of_birth']
def __init__(self, *args, **kwargs):
super(MyModelForm, self).__init__(*args, **kwargs)
my_user = kwargs.get('instance')
first_name = my_user.user.first_name
self.fields['first_name'].initial = first_name #this will show the first name in the html page when i request the instance
And when I'm instantiating my form I just pass an instance of MyModel as a
kwarg: form = MyModelForm(instance=my_model_instance)
You can use InlinFormset. Here I am posting a code sample used by me that might help you. I have modified a bit according to your need, so if anything doesn't work let me know.
#forms.py
class UserForm(forms.ModelForm):
class Meta:
model = User
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
then in your views you should do like
def profileEdit(request,username):
# querying the User object with pk from url
user = User.objects.get(username=username)
# prepopulate MyModelForm with retrieved user values from above.
user_form = UserForm(instance=user)
MyModelInlineFormset = inlineformset_factory(User, MyModel,can_delete=False, fields="__all__")
formset = MyModelInlineFormset(instance=user)
if request.user.is_authenticated() and request.user.id == user.id:
if request.method == "POST":
user_form = UserForm(request.POST, request.FILES, instance=user)
formset = MyModelInlineFormset(request.POST, request.FILES, instance=user)
if user_form.is_valid():
created_user = user_form.save(commit=False)
formset = MyModelInlineFormset(request.POST, request.FILES, instance=created_user)
if formset.is_valid():
created_user.save()
formset.save()
return HttpResponseRedirect('/')
return render(request, "profile_edit.html", {
'title':'Edit -'+ user.get_full_name(),
"user_form": user_form,
"formset": formset,
})
else:
raise PermissionDenied
and in your profile_edit.html
{% extends "layout.html" %}
{% block content %}
{% if messages %}
<div>
{% for message in messages %}
<div class="alert alert-{{ message.tags }}"> <!-- singular -->
<a class="close" data-dismiss="alert">×</a>
{{ message|safe }}
</div>
{% endfor %}
</div>
{% endif %}
<div class="col-xs-8 col-xs-offset-2">
<div class="card">
<div class="card-content">
<h2 class="flow-text">Update your information</h2>
<form action="." method="POST" class="padding" enctype="multipart/form-data">
{% csrf_token %}
{% for field in user_form %}
<div class="form-group">
{{ field.errors }}
<label for="{{ field.label }}" >{{ field.label }}</label>
{{ field }}
{% if field.help_text %}
<small class="form-text text-muted">{{ field.help_text|safe }}</small>
{% endif %}
</div>
{% endfor %}
{{ formset.management_form }}
{% for form in formset %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in form.visible_fields %}
<div class="form-group">
{{ field.errors }}
<label for="{{ field.label }}" >{{ field.label }}</label>
{{ field }}
{% if field.help_text %}
<small class="form-text text-muted">{{ field.help_text|safe }}</small>
{% endif %}
</div>
{% endfor %}
{% endfor %}
<input type="submit" class="btn btn-primary" value="Save"></input>
</form>
</div>
</div>
</div>
{% endblock content %}
Related
I am using Django 4.0.3 with a bootstrap webinterface.
For layout reasons I want my fields in a ModelForm to be grouped and I'm doing it with:
class UserForm(ModelForm):
template_name = "container/form.html"
field_groups = [["email", "company"], ["last_name", "first_name"]]
grouped_fields = []
class Meta:
model = MyUser
fields = ["email", "company", "first_name", "last_name"]
__init__(self, *args, **kwargs):
super(UserForm, self).__init__(*args, **kwargs)
def group_fields(self):
for group in self._field_groups:
group_entry = []
for entry in group:
group_entry.append(self.fields[entry])
self.grouped_fields.append(group_entry)
in the view I initialize my form and the regroup the fields:
def user_form(request):
form = UserForm()
form.group_fields()
render(request, "page.html, {"form", form})
The page.html looks like this:
<body>
<div id="form-wrapper">
{{ form }}
</div>
</body>
and the form.html looks like this:
<form action="" method="POST">{% csrf_token %}
{% for field_group in form.grouped_fields %}
<div class="row">
{% for field in field_group %}
<div class="col">
<label>{{ field.label }}</label> {{ field }}
</div>
{% endfor %}
</div>
{% endfor %}
However the rendered fields are displayed as string representations of the field objects:
Email <django.forms.fields.EmailField object at 0x7f98c00e03a0>
Company <django.forms.fields.CharField object at 0x7f98c00e0250>
Last name <django.forms.fields.CharField object at 0x7f98c00e0790>
First name <django.forms.fields.CharField object at 0x7f98c00e10c0>
Whereas a common call renders as expected a form with input fields, but not with the desired layout.
{% for field in form %}
<div class="row">
<div class="col">
{{ field.label_tag }} {{ field }}
</div>
</div>
{% endfor %}
I also tried to move the call of the group_fields method to the form init but had no success.
Is there a way to render the fields, that are stored in my grouped_fields container correctly?
After quiet a time of trying this and that I ended up writing a wrapper for ModelForm, define my field_groups there and rendered it in the template.
It is not nice, but it does the trick.
<form action="" method="post">
{% for fieldgroup in form.field_groups %}
<div class="row fieldgroup">
{% for fieldname in fieldgroup %}
{% for field in form %}
{% if field.name == fieldname %}
<div class="col pt-2">
<label class="form-label">{{ field.label }}</label>
{{ field }}
</div>
{% endif %}
{% endfor %}
{% endfor %}
</div>
{% endfor %}
<button type="submit">Submit</button>
</form>
Nothing i do seems to work out for me. I tired different models and forms but ultimately i get weird errors so all i can provide is the default code.
forms.py
from django import forms
class CommentForm(forms.Form):
author = forms.CharField(
max_length=60,
widget=forms.TextInput(attrs={
"class": "form-control",
"placeholder": "Your Name"
})
)
body = forms.CharField(widget=forms.Textarea(
attrs={
"class": "form-control",
"placeholder": "Leave a comment!"
})
)
views.py
def blogdetail(request, pk):
post = Post.objects.get(pk=pk)
comments = Comment.objects.filter(post=post)
form = CommentForm()
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = Comment(
author=form.cleaned_data["author"],
body=form.cleaned_data["body"],
post=post,
)
comment.save()
context = {"post": post, "comments": comments, "form": form}
return render(request, "blogdetail.html", context)
blogdetail.html
{% extends "base.html" %}
{% block page_content %}
<div class="col-md-8 offset-md-2">
<h1>{{ post.title }}</h1>
<small>
{{ post.created_on.date }} |
Categories:
{% for category in post.categories.all %}
<a href="{% url 'blogcategory' category.name %}">
{{ category.name }}
</a>
{% endfor %}
</small>
<p>{{ post.body | linebreaks }}</p>
<h3>Leave a comment:</h3>
<form action="/blog/{{ post.pk }}/" method="post">
{% csrf_token %}
<div class="form-group">
{{ form.author }}
</div>
<div class="form-group">
{{ form.body }}
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<h3>Comments:</h3>
{% for comment in comments %}
<p>
On {{comment.created_on.date }}
<b>{{ comment.author }}</b> wrote:
</p>
<p>{{ comment.body }}</p>
<hr>
{% endfor %}
</div>
{% endblock %}
i want to relace author with the username of the user who wrote that comment. How so? what do i need to change to make this happen?
im new to Django. Im creating a bidding site, where users should be able to visit a page to create a new listing (item that they are going to put up).The form that they are required to submit has a few common fields and when valid, the said form should be saved.
Problem is, my listingcreateview() is not doing that. I submit a correct form but it wont save, it just redirects to the same form page and No errors are shown.
This because the submitted form is validated as invalid every time. I know this because of the two functions i added inside listingcreateview(), the second one is called.
It was working properly before, dont know what changes messed it up. If i add in the admin interface information by hand, it is saved successfully.
views.py:
class ListingCreateView(CreateView):
model = Listing
fields = ['title', 'content', 'image', 'min_bid', 'categories']
def form_valid(self, form):
form.instance.seller = self.request.user
return super().form_valid(form)
def form_invalid(self, form):
return HttpResponseRedirect(reverse("index"))
models.py:
class User(AbstractUser):
pass
class Listing(models.Model):
id = models.IntegerField(primary_key=True)
title = models.CharField(max_length=100)
image = models.ImageField(blank=False, upload_to='media')
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
categories = models.CharField(max_length=25, choices = category)
seller = models.ForeignKey(User, on_delete=models.CASCADE) ##
min_bid = models.FloatField(blank=False)
image_thumbnail = ImageSpecField(source='image', processors=[ResizeToFill(300, 150)], format='JPEG', options={'quality':100})
def get_absolute_url(self):
return reverse('listing-detail', kwargs={'pk': self.pk})
listing_form.html:
{% extends "auctions/layout.html" %}
{% block body %}
<h2> Create Listing </h2>
{% if message %}
<div>{{ message }}</div>
{% endif %}
{% if messages %}
<div class="alert alert-warning" role="alert">
{{ messages }}
</div>
{% endif %}
<div class="container">
<form method="POST" action="">
{% csrf_token %}
<label class="label">{{ form.title.label }}</label>
<div class="input">{{ form.title }}</div>
<label class="label">{{ form.content.label }}</label>
<div class="input">{{ form.content }}</div>
<label class="label">{{ form.image.label }}</label>
<div class="input">{{ form.image }}</div>
<label class="label">Minimal bid</label>
<div class="input">{{ form.min_bid }}</div>
<label class="label">{{ form.categories.label }}</label>
<div class="input">{{ form.categories }}</div>
<input type="submit" value="Submit">
</form>
</div>
{% endblock %}
urls.py:
path("create-listing", login_required(ListingCreateView.as_view()), name="create-listing")
Your form is invalid because form is missing
enctype="multipart/form-data" which is needed for file uploads
I have a model.py:
class Author(models.Model):
Author= models.CharField(max_length=500) ##field required
and a form.py:
class AuthorForm(forms.ModelForm):
class Meta:
model = Author
widgets = {
'Author': TextInput(attrs={'class':'form-control', 'placeholder': 'Author...'}),
in template I render whit:
{{ form.Author }}
Form works but if I try to save leave the field empty it not display any error.
I try with
{{ form.Author.errors }}
but nothing appear!
Any idea?
TY
UPGRADE 1
simple view:
#login_required
def auth_reg(request):
if request.method == "POST":
form = AuthorForm(request.POST)
if form.is_valid():
member = form.save()
return redirect('home')
else: form = AuthorForm()
return render(request, auth_reg.html', {'form': form})
and simple template:
{% extends 'base.html' %}
{% block content %}
<div id="page-content-wrapper">
<form method="post">
{% csrf_token %}
<div id="page-title">
<h2>Registration</h2>
</div>
<div class="content-box-wrapper">
<div class="form-horizontal bordered-row">
<div class="form-group">
<label class="col-sm-3 control-label">Author</label>
<div class="col-sm-3">
{{ form.Author}}
</div>
</div>
</div>
</div>
</div>
I have searched some posts about this issue but still not find solution. After clicking the "submit", the page could redirect to another page. But the sql database doesn't show the submitted information.
Thanks in advance for your suggestion.
views.py
#csrf_exempt
def input(request):
if request.method == 'POST':
form = InputForm(request.POST or None, request.FILES or None)
if form.is_valid():
company = form.cleaned_data['company']
region = form.cleaned_data['region']
uom= form.cleaned_data['uom']
start_date= form.cleaned_data['start_date']
end_date= form.cleaned_data['end_date']
add_input=Input.objects.create(company=company,region=region,uom=uom,start_date=start_date,end_date=end_date)
add_input.save()
return redirect('resut')
else:
print(form.errors)
else:
form = InputForm(initial={'company':'coco','uom':'M$'},instance=Input)
return render_to_response('inputform.html',{'form': form})
models.py
class Input(models.Model):
company=models.CharField(max_length=100)
region=models.CharField(max_length=100)
uom=models.CharField(max_length=100)
start_date=models.DateField(auto_now=False, auto_now_add=False)
end_date=models.DateField(auto_now=False, auto_now_add=False)
forms.py
class InputForm(forms.ModelForm):
company=forms.CharField(widget=forms.TextInput, label="Company",error_messages={'required': 'Please enter your name'},required=True)
regionlist = forms.ModelChoiceField(queryset=Dupont.objects.values('region').distinct())
uom=forms.CharField(required=True)
start_date=forms.DateField(widget=DateInput(),required=True)
end_date = forms.DateField(widget=DateInput(),required=True)
error_css_class='error'
required_css_class = 'required'
class Meta:
model = Input
fields = ('company', 'region','uom','start_date','end_date')
widgets = {
'start_date': forms.DateInput(attrs={'class':'datepicker'}),
'end_date': forms.DateInput(attrs={'class':'datepicker'}),
}
html snippet
<form action="{% url 'result' %}" method="post">{% csrf_token %}
<!--company-->
<div class="field">
<p>Company:<input type="text" name="company" value="{{company}}"/>
</div>
<!--region-->
<div class="field" >
<label> Select the Region:
{{ form.regionlist }}
{% for region in form.regionlist.choices %}
<option value="{{ val }}" {% ifequal data.val val %}selected {% endifequal %}></option>
{% endfor %}
</label>
</div>
<!--uome-->
<div class="field">
<p>Unit of Measure:<input type="text" name="uom" value="{{uom}}"/>
</div>
<!--start date-->
<label for="startDate">Start Month:</label>
<input name="start_date" id="startDate" class="date-picker"/>
<!--END date-->
<label for="endDate">End Month:</label>
<input name="end_date" id="endDate" class="date-picker" />
<!--submit-->
<div class="fieldWrapper">
<p><input type="submit" value="Submit" /></p></div>
{% if form.errors %}
{% for field in form %}
{% for error in field.errors %}
<div class="alert alert-error">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endfor %}
{% for error in form.non_field_errors %}
<div class="alert alert-error">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endif %}
</form>
No, the action point to another view, instead of input view. I don't paste that view because the issue now is this form's data doesn't saved successfully.
Well then thats your problem, when you submit a form it goes to the specified action. If the snippet is the inputform.html as I suspect it is then you just need to delete the action from your form.
<form method="post">