I'm trying to ask a user some additional info while signing up. I'm using django allauth for authorization and authentication. I try to add three more fields during the signup process. If If I run it, it shows me the standard form plus gender field. However, it doesn't seem to really work. How can I save the data? Could someone help? Thank you in advance!
EDITED: if I just use
if form.is_valid():
form.save()
return redirect('/success/')
I get an error:
save() missing 1 required positional argument: 'user'
I'm quite new to django.
I created signups app in the project.
I put this in allauth_settings.py:
ACCOUNT_SIGNUP_FORM_CLASS = 'signups.forms.MySignupForm'
My signups/model.py:
from django.contrib.auth.models import User
from django.db import models
from allauth.account.models import EmailAddress
from allauth.socialaccount.models import SocialAccount
import hashlib
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile')
about_me = models.TextField(null=True, blank=True)
timestamp = models.DateTimeField(auto_now_add= True, auto_now=False)
updated = models.DateTimeField(auto_now_add= False, auto_now=True)
GENDER_CHOICES = (
('m', 'Male'),
('f', 'Female'),
)
# gender can take only one of the GENDER_CHOICES options
gender = models.CharField(max_length=1, choices=GENDER_CHOICES,
verbose_name='Gender')
def __unicode__(self):
return self.user.username
class Meta:
db_table = 'user_profile'
def profile_image_url(self):
"""
Return the URL for the user's Facebook icon if the user is logged in via
Facebook, otherwise return the user's Gravatar URL
"""
fb_uid = SocialAccount.objects.filter(user_id=self.user.id, provider='facebook')
if len(fb_uid):
return "http://graph.facebook.com/{}/picture?width=40&height=40".format(fb_uid[0].uid)
return "http://www.gravatar.com/avatar/{}?s=40".format(hashlib.md5(self.user.email).hexdigest())
def account_verified(self):
"""
If the user is logged in and has verified hisser email address, return True,
otherwise return False
"""
if self.user.is_authenticated:
result = EmailAddress.objects.filter(email=self.user.email)
if len(result):
return result[0].verified
return False
User.profile = property(lambda u: UserProfile.objects.get_or_create(user=u)[0])
my signups/forms.py:
from allauth.account.forms import SignupForm
from django import forms
from .models import UserProfile
class MySignupForm(SignupForm):
class Meta:
model = UserProfile
gender = forms.CharField(max_length=1, label='gender')
def save(self, user):
user.gender = self.cleaned_data['gender']
user.save()
my signups/views.py:
from django.template import RequestContext
from django.shortcuts import render_to_response
from .forms import SignupForm
def index(request):
form = MySignupForm(request.POST or None)
if form.is_valid:
???
return render_to_response("signups/index.html", locals(),
context_instance=RequestContext(request))
My index.html is very basic, I just wanted to see the representation of the form:
{% extends 'account/base.html' %}
{% block head_title %}ProjectName{% endblock %}
{% block content %}
<form method="POST" action="">
{{ form.as_p }}
<input type="submit">
</form>
{% endblock %}
You are instantiating the SignupForm, which is the standard form but not your MySignupForm in the view. Change it like this:
def index(request):
form = MySignupForm()
return render_to_response("signups/index.html", locals(),
context_instance=RequestContext(request))
Related
I'm creating a staff registration page to create a new user (where NO one has logged in i.e. an Anonymous User). I have a Profile model (adding additional fields such as department, alias etc) which extends from the User model within Django. I've referenced User as a one-to-one relationship to Profile.
When a new staff registers, they have to specify their "stafftypeid" ("Job Title") from the ModelChoiceField referencing Staff_Type table. The User table doesn't store "stafftypeid", so I extended the UserCreationForm.
My problem is that I can successfully submit POST variables via the forms and the User (auth_user) will create a new record. I can see the new User within the Django /admin page. However, Profile will fail to create an accompanying new record and I receive an error ("IntegrityError at /register/
(1048, "Column 'staffTypeID' cannot be null")"). Strangely, I have all the POST variables, but the variables needed for the fields in Profile table are not being passed across.
Is this an inheritance problem? A new Profile record should only be created when you create a new User.
I've tried to follow tutorials and other code from Corey Schafer Django Tutorial 8, Simpleisbetterthancomplex (https://simpleisbetterthancomplex.com/tutorial/2016/07/22/how-to-extend-django-user-model.html#onetoone) and Stack Overflow.
I've also tried re-writing def form.save() and then split my forms into two (originally just one), since its supposed to be easier to handle on the database side. I'd be really grateful for advice here!
Administrator\models.py: (Staff_Type)
from django.db import models
from decimal import Decimal
class Staff_Type(models.Model):
stafftypeid = models.AutoField(db_column='staffTypeID', primary_key=True)
stafftypedesc = models.CharField(db_column='staffTypeDesc', max_length=150)
class Meta:
ordering = ['stafftypedesc']
unique_together = ('stafftypedesc',)
db_table = 'stafftype'
def __str__(self):
return self.stafftypedesc
Users\models.py:
from django.db import models
from django.contrib.auth.models import User
from decimal import Decimal
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
stafftypeid = models.ForeignKey('Administrator.Staff_Type', models.DO_NOTHING, db_column='staffTypeID')
employeeid = models.CharField(max_length=20)
alias = models.CharField(max_length=20)
department = models.CharField(max_length=150)
organization = models.CharField(max_length=150)
fte = models.DecimalField(max_digits=4, decimal_places=1, default=Decimal(100.0))
def __str__(self):
return f'{self.user.username} Profile'
Users\signals.py:
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.dispatch import receiver
from .models import Profile
#receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_profile(sender, instance, **kwargs):
instance.profile.save()
Users\register.html:
{% extends "Administrator/adminBase.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="col-md-8">
<div class="content-section">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Register New User </legend>
{{ user_form|crispy }}
{{ profile_form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Sign Up</button>
</div>
</form>
</div>
</div>
{% endblock content %}
Users\forms.py:
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from Administrator.models import Staff_Type
from .models import Profile
from .models import Profile
from django.utils.translation import ugettext_lazy as _
class UserRegisterForm(UserCreationForm):
email = forms.EmailField(max_length=150, label = "Email")
first_name = forms.CharField(max_length=150, label = "First Name")
last_name = forms.CharField(max_length=150, label = "Surname")
class Meta:
model = User
fields = ['username', 'email', 'first_name', 'last_name', 'password1', 'password2']
class ProfileForm(forms.ModelForm):
stafftypeid = forms.ModelChoiceField(queryset=Staff_Type.objects.all(), empty_label="Staff Type")
employeeid = forms.CharField(max_length=20, label="Employee ID")
alias = forms.CharField(max_length=20, label="Alias")
department = forms.CharField(max_length=150, label="Department")
organization = forms.CharField(max_length=150, label="Organization")
fte = forms.DecimalField(max_digits=4, decimal_places=1, min_value=0.0, max_value=100.0, label="FTE(%)")
class Meta:
model = Profile
fields = ['stafftypeid', 'employeeid', 'alias', 'department', 'organization', 'fte']
Users\views.py:
from django.shortcuts import render, redirect
from django.contrib import messages
from .forms import UserRegisterForm, ProfileForm
def register(request):
if request.method == "POST":
user_form = UserRegisterForm(request.POST)
profile_form = ProfileForm(request.POST)
if user_form.is_valid() and profile_form.is_valid():
user_form.save()
profile_form.save()
username = user_form.cleaned_data.get('username')
stafftypeid = profile_form.cleaned_data.get('stafftypeid')
messages.success(request, f'Account created for {username}, with alias: {stafftypeid}')
return redirect('admin-home')
else:
user_form = UserRegisterForm()
profile_form = ProfileForm()
return render(request, 'users/register.html', {'user_form': user_form, 'profile_form': profile_form})
Users\apps.py:
from django.apps import AppConfig
class UsersConfig(AppConfig):
name = 'users'
def ready(self):
import users.signals
The error is happening in your signal. There you just create a Profile and only set the user field, you don't set any of the other fields.
You don't need this signal. You are creating the profile in the separate Profile form and saving it in the view. You should remove both those signals.
Then, update the view:
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save()
profile = profile_form.save(commit=False)
profile.user = user
profile.save()
(Separately, please stop naming your ForeignKeys as ending in "_id"; they're not IDs, they are the actual related objects.)
I have a small problem with adding data to the database in django 2.0.3
I created the following model:
from django.contrib.auth.models import User
class UserInputSignal(models.Model):
name = models.CharField(max_length=512)
author = models.ForeignKey(User, on_delete=models.CASCADE)
input_file = models.FileField(upload_to='signals/', null=True)
I tried to solve the problem using this form:
from django import forms
from .models import UserInputSignal
class UserInputSignalForm(forms.ModelForm):
name = forms.CharField()
input_file = forms.FileField()
class Meta:
model = UserInputSignal
fields = ('name', 'input_file', )
and this view:
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from django.contrib.auth import authenticate
from .forms import UserInputSignalForm
#login_required
def storage(request):
form = UserInputSignalForm(request.POST or None)
if request.method == 'POST':
if form.is_valid():
name = request.POST.get('name')
author = request.POST.get(request.user)
input_file = request.POST.get('input_file')
return redirect('home')
else:
form = UserInputSignalForm()
return render(request, 'storage.html', {'form': form})
In the template I called, I created the form as follows:
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Upload</button>
</form>
I am able to load a page with a form, but it does not post data to the database. I would like to add that I am a novice in django and some mechanisms are just plain understandable for me. Can I ask someone for help with this problem?
Before the redirect, call form.save()
Okay, i worked on your code and it works with me with slight modifications:
models.py
class UserInputSignal(models.Model):
name = models.CharField(max_length=512)
author = models.ForeignKey(User, on_delete=models.CASCADE)
input_file = models.FileField(upload_to='signals/', null=True)
objects = models.Manager()
#this returns the name for your modelobject
def __str__(self):
return self.name
forms.py
#excluded the assiging as fields defination is enough in itself
class UserInputSignalForm(forms.ModelForm):
class Meta:
model = UserInputSignal
#this will exclude the formfield it self but the author will be saved as the person who is logged in
exclude = ["author"]
Edited - Views.py
#login_required
def storage(request):
#authentication for author field using request.user
insta = UserInputSignal(author=request.user)
print(request.user)
form = UserInputSignalForm(request.POST or None, request.FILES or None,instance=insta)
if request.method == 'POST':
if form.is_valid():
signal = form.save(commit=False)
signal.save()
return redirect('home')
else:
form = UserInputSignalForm(instance=insta)
return render(request, 'storage.html', {'form': form})
JlucasRs was right to tell you to use form.save(), but you needed to assign form to something and need not use model fields here as forms.py does that for you.
app/Urls.py - Just for reference
urlpatterns = [
path('home/', home, name='home'),
path('storage/', storage, name='storage'),
]
Edit- Admin.py
from .models import PostModel, UserInputSignal
class UserInputSignalAdmin(admin.ModelAdmin):
list_display = ('name', 'author', 'input_file' )
admin.site.register(UserInputSignal, UserInputSignalAdmin)
Add this code in Admin.py if its not there.
So I have created a a dynamic formset that allows the user to add or remove as many forms as they want. Now, I am trying to do custom validation in a field(block_name) in the forms.
It seems like it is working because if the user input doesn't match a certain regex, then the data will not be saved. The problem is that there is no message showing that the input is wrong.
Also, if the user inputs incorrect data and tries to submit the page will redirect to itself and erase all the input data. How can I make the page stay in the same view if the input is wrong and also show error messages?
forms.py:
import re
from django import forms
from django.forms.models import modelformset_factory
from inventory.models import Block
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
# Form for block requests
class BlockForm(forms.ModelForm):
block_name = forms.CharField(required=True, validators= [RegexValidator('^s\d{3}rf\d*b\d*e\d+r\d+w\d*[cgls][abcdex][ed][hv][sbaec][a-d] [a-d][0-7][apfg]a', message="Please enter valid block name", code="invalid_name")])
def __init__(self, *args, **kwargs):
super(BlockForm, self).__init__(*args, **kwargs)
self.empty_permitted = False
class Meta:
model = Block
fields = ['block_name', 'block_derivatives', 'block_subsystems', 'owners']
def clean_block_name(self):
print self.cleaned_data
block_name = self.cleaned_data.get('block_name')
if block_name == "a":
print ("block name is a")
raise forms.ValidationError(
('Please enter a block name'))
return block_name
models.py:
import datetime
import re
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
from django.db import models
from django.utils import timezone
class Inventory(models.Model):
class Meta:
verbose_name_plural = "Inventories"
inventory_name = models.CharField(max_length=100)
pub_date = models.DateTimeField('date published')
def __str__(self): # __unicode__ on Python 2
return self.inventory_name
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
class Block(models.Model):
class Meta:
verbose_name_plural = "Request Blocks"
inventory = models.ForeignKey(Inventory, null=True)
block_status = models.CharField(max_length=20, blank=False)
#617block_name = models.CharField(max_length=50, blank=False, null=False, validators=[block_nameTest])
block_name = models.CharField(max_length=50, blank=False, null=False)
block_derivatives = models.CharField(max_length=100)
block_subsystems = models.CharField(max_length=40)
owners = models.CharField(max_length=100)
def __str__(self):
return self.block_name
def block_owners(self):
return str(self.owners)
views.py:
from django.forms.models import modelformset_factory
from django.forms.formsets import formset_factory
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from django.views import generic
from django.http import HttpResponse
from django.shortcuts import render_to_response
from .models import Inventory, Block
from .forms import BlockForm
# Create your views here.
def requests(request, inventory_id):
BlockFormSet = formset_factory(BlockForm, extra=1)
inventory = get_object_or_404(Inventory, pk=inventory_id)
formset = BlockFormSet(request.POST)
if request.method == 'POST':
formset = BlockFormSet(request.POST)
if formset.is_valid():
for form in formset:
print form
form.save()
print "Success"
return HttpResponseRedirect('/inventory/2')
else:
print "Yo, this stuff is not validated"
else:
print "LOLOLOLOL"
return render(request, 'inventory/requests.html', {'inventory': inventory, 'formset': BlockFormSet})
requests.html:
{% block content %}
<div class="requestForm">
<form id="blockForm" class="original" action="{% url 'inventory:requests' inventory.id %}" method="post">{% csrf_token %}
<!-- Add New Row -->
{{formset.management_form}}
{% for form in formset %}
<div class='item'>
<ul>{{ form.as_table}}<ul>
<p style=""><a class="delete" href="#">Delete</a></p>
</div>
{% endfor %}
<p><a id="add" href="#">Add another item</a></p>
<input type="submit" name="submit" value="Request Blocks" id="submitButton">
</form>
</div>
{% endblock %}
You're redirecting after an invalid POST, rather than redisplaying the same form. Even though your redirect is to the same view, it loses the POST data and the form is therefore blank.
Drop the first else block, and let execution fall through to the final render line.
My problem is similar to how to edit model data using django forms, but I'm not able to solve it.
I would like to get an form with prefielled fields and to allow user to edit them.
I believe my problem is in views.py file, but unfrotuntely I'm not able to solve it.
models.py
from django.db import models
class Item(models.Model):
product = models.CharField(max_length=150)
quantity = models.DecimalField(max_digits=8, decimal_places=3)
price = models.DecimalField(max_digits=7, decimal_places=2)
purchase_date = models.DateTimeField()
warranty = models.DecimalField(max_digits=4, decimal_places=1)
comment = models.TextField()
forms.py
from django import forms
from items.models import Item
class EditItemForm(forms.ModelForm):
class Meta:
model = Item
fields = ('product','quantity', 'price', 'purchase_date', 'warranty', 'comment')
urls.py
from django.conf.urls import patterns, include, url
urlpatterns = patterns('',
url(r'^show_all/$', 'items.views.items'),
url(r'^(?P<item_id>\d+)/$', 'items.views.item'),
url(r'^edit/(?P<item_id>\d+)/$', 'items.views.edit'),
)
edit.html
<form action="/items/edit/" method="post" class="form horizontal well">{% csrf_token %}
{{ form.as_p }}
<imput type="submit" class="btn btn-inverse" value="Aktualizuj">
</form>
views.py
from django.shortcuts import render_to_response
from django.http import HttpResponseRedirect
from items.models import Item
from decimal import Decimal
from django.core.context_processors import csrf
from items.forms import EditItemForm
def edit(request):
if request.method == 'POST':
form = EditItemForm(request.POST, instance=request.item)
if form.is_valid():
form.save()
return HttpResponseRedirect('/items/show_all/')
else:
form = EditItemForm(instance=item)
args = {}
args.update(csrf(request))
args['form'] = form
return render_to_response('edit.html', args)
Above code is resulting this message:
TypeError at /items/edit/1/
edit() got an unexpected keyword argument 'item_id'
Can you please help me?
Django 1.6, Python 3.4
You've imagined an attribute called request.item. There's no such thing. You need to get the item from the database, via the ID passed into the function as alecxe showed.
def edit(request, item_id):
item = Item.objects.get(pk=item_id)
if request.method == 'POST':
form = EditItemForm(request.POST, instance=item)
edit() view should allow a keyword argument item_id:
def edit(request, item_id=None):
if request.method == 'POST':
...
I'm trying to add a new feature to my existing app that let users create a profile and upload a pictures of their pets.
When a user login , he gets redirected into the profile which display his name and also he can add a picture of himself into the model which will get displayed on the profile page.
At the moment , I can retrieve the name into the template but I can't seem to display the user's name and upload picture at the same time.
Whenever I click Add picture , It doesn't let the user upload a picture instead I get this error
'PictureForm' object has no attribute 'save'
pet = form.save(commit =False) ...
I could design the page to let the user upload a picture but not display the name at the same time.
I think the problem lays in my profile.html and Profile function at views.py
Parts of my views.py
#login_required
def Profile(request):
Person = request.user.get_profile()
if not request.user.is_authenticated():
return HttpResponseRedirect('/login/')
if request.method == "POST":
form = PictureForm(request.POST ,request.FILE or None)
if form.is_valid():
pet = form.save(commit =False)
pet.save()
context = (
{'Person': Person} ,
{'form':PictureForm()}
)
return render_to_response('profile.html', context, context_instance=RequestContext(request))
Parts of my forms.py
from django import forms
from django.contrib.auth.models import User
from django.forms import ModelForm
from pet.models import *
class PictureForm(forms.Form):
class Meta:
model = Person
fields = ('image')
My profile.html
{% if Person %}
<ul>
<li>Name : {{Person.name}} </li>
</ul>
{% endif %}
<form method="POST" enctype="multipart/form-data" "action" >
{% csrf_token %}
<ul>
{{ form.as_ul }}
</ul>
<input type = "submit" value= "Add Picture" />
</form>
My models.py
from django.db import models
from django.db.models.signals import post_save
from django.contrib.auth.models import User
class Person(models.Model):
user = models.OneToOneField(User)
name = models.CharField(max_length=100)
image = models.FileField(upload_to="images/",blank=True,null=True)
def __unicode__(self):
return self.name
class Pet(models.Model):
Person = models.ForeignKey(Person)
description = models.CharField(max_length=100)
image = models.FileField(upload_to="images/",blank=True,null=True)
def __unicode__(self):
return self.description
PictureForm needs to inherit from forms.ModelForm, not forms.Form.
Erase your form.save(commit=False). You will only do that if you override your save method
#login_required
def Profile(request):
Person = request.user.get_profile()
if not request.user.is_authenticated():
return HttpResponseRedirect('/login/')
if request.method == "POST":
form = PictureForm(request.POST ,request.FILES)
if form.is_valid():
form.save()
context = (
{'Person': Person} ,
{'form':PictureForm()}
)
return render_to_response('profile.html', context, context_instance=RequestContext(request))
UPDATE:
[.....]
board = Board.objects.get(board=picture.board)//remove this
the_id = board.id //remove this
return HttpResponseRedirect(reverse('world:Boat', kwargs={'animal_id': picture.board.id })) // change the_id into picture.board.id
You have a typo. It should be request.FILES.
no buddy. your problem is in your model.py:
just add this function to your model
def save(self,*args, **kw):
super(PictureForm,self).save(*args, **kw)