I have been working on a view in which all of the users profiles are displayed but so that a user can see other peoples profiles in that page, the user has to first create a profile. So every user who has already created a profile can go to that page but if the user havent created one yet, then the user goes to a form until it has a profile. To make this happen, I am using if statments to determine if the user has a profile or not but this is not working because every user is being redirected to the form part of the page even if the user has already a profile. How can this error be fixed, is there something wrong with the database? Is there other way to make these happen besides using if statements on the html?
models.py
class Mates(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='usermates')
users_requests = models.ManyToManyField(User, related_name="users_requests")
req_bio = models.CharField(max_length=400)
req_image = models.ImageField(upload_to='requestmates_pics', null=True, blank=True, default=False)
views.py
def matesmain(request):
contents = Mates.objects.all()
context = {
'contents': contents,
'form_mates': MatesForm(),
}
print("nice3")
return render(request, 'mates.html', context)
def mates(request):
if request.method == 'POST':
form_mates = MatesForm(request.POST, request.FILES)
if form_mates.is_valid():
instance = form_mates.save(commit=False)
instance.user = request.user
instance.save()
return redirect('mates-main')
print('succesfully uploded')
else:
form_mates = MatesForm()
print('didnt upload')
context = {
'form_mates': form_mates,
'contents': Mates.objects.all()
}
return render(request, 'mates.html', context)
urls.py
urlpatterns = [
path('mates', views.mates, name='mates'),
path('mates-main', views.matesmain, name='mates-main'),
]
mates.html
{% if not contents.user == user %}
FORM GOES HERE
{% elif contents.user == user %}
{% for content in contents %}
USER PROFILES HERE
{% endfor %}
{% endif %}
If there are any questions or need to see more code please let me know in the comments:)
Mates.objects.all() is returning you every User in your database, but objects.all() doesn't have a method called .user(), you should use something like:
Mate.objects.get(name__exact='username')
You could look a the django docs to know more about how to make queries
https://docs.djangoproject.com/en/3.0/topics/db/queries/
Related
I have been trying to create a view that lets a user create a "profile" but if the user already has a profile then the user is redirected to page where the user can see other people's profiles(in order to see this other people's profiles, the user has to create a profile as a requirement), for doing this proces I have 2 templates, one that has a form to create the profile and other one that displays other user's profile. The error is that every user is redirected to mates-form.html even the ones that already have a profile. So far I think that the error is on the views.py file.
models.py
class Mates(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='usermates')
users_requests = models.ManyToManyField(User, related_name="users_requests")
req_bio = models.CharField(max_length=400)
req_image = models.ImageField(upload_to='requestmates_pics', null=True, blank=True, default=False)
views.py
def matesmain(request):
contents = Mates.objects.all()
if contents == request.user:
context = {
'contents': contents,
'form_mates': MatesForm(),
}
print("nice3")
return render(request, 'mates.html', context)
else:
return render(request, 'mates-form.html')
def mates(request):
if request.method == 'POST':
form_mates = MatesForm(request.POST, request.FILES)
if form_mates.is_valid():
instance = form_mates.save(commit=False)
instance.user = request.user
instance.save()
return redirect('mates-main')
print('succesfully uploded')
else:
form_mates = MatesForm()
print('didnt upload')
context = {
'form_mates': form_mates,
'contents': Mates.objects.all()
}
return render(request, 'mates-form.html', context)
forms.py
class MatesForm(forms.ModelForm):
class Meta:
model = Mates
fields = ('req_bio', 'req_image',)
exclude = ['user']
mates.html
{% if contents %}
{% for content in contents %}
Here is where the user can see other user's profiles
{% endfor %}
{% endif %}
mates-form.html
<form method="post" enctype="multipart/form-data">
{{ form_mates.as_p }}
</form>
If you have any questions or if you need to see more code please let me know in the comments, also I thought of other way for doing these removing the if statements from matesmain view and just using them on the html but that didnt work.
I suppose the user will have only one profile so Instead of ManyToOneRelation i.e. ForeignKey using OneToOneRelation with the User Model would be better.
class Mates(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='usermates')
Now while creating the profile you can check whether the user profile already exists or not like this:
def mates(request):
if Mates.objects.filter(user=request.user).exists():
return redirect('redirect_to_some_view_you_want')
if request.method == 'POST':
form_mates = MatesForm(request.POST, request.FILES)
I am trying to save users IP address in my extended profile model. The goal is to make this a hidden field. Currently, I can debug by printing the IP address to the console. The issue arises when I try and save the info.
views.py
def index(request):
#request.session.flush()
if request.user.is_authenticated:
return redirect('ve:dashboard')
elif request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
user = form.save(commit=False)
user.refresh_from_db() # Load the profile instance created by the Signal
user.profile.birth_date = form.cleaned_data.get('birth_date')
user.ipaddress = get_ip(request)
print(user.ipaddress)
user.save()
raw_password = form.cleaned_data.get('password1')
user = authenticate(username=user.username, password=raw_password)
login(request, user)
return redirect('ve:dashboard')
else:
form = RegistrationForm()
return render(request, 'index.html', {'form': form})
forms.py
class RegistrationForm(UserCreationForm):
# birth_date = forms.DateField(help_text='Required. Format: YYYY-MM-DD')
birth_date = forms.DateField(widget=SelectDateWidget(years=range(1999, 1910, -1)))
#ipaddress = forms.IntegerField(widget=forms.HiddenInput(), required=False)
class Meta:
model = User
fields = ('username', 'email', 'birth_date', 'password1', 'password2',)
exclude = ['ipaddress',]
index.html
<form method="post">
{% csrf_token %}
{% for field in form %}
<p class="text-left">
{{ field.label_tag }}<br>
{{ field }}
{% for error in field.errors %}
<p style="color: red">{{ error }}</p>
{% endfor %}
</p>
{% endfor %}
<button type="submit">Sign up</button>
</form>
models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
...
ipaddress = models.CharField(default="0.0.0.0", max_length=30)
This form was working fine before I tried adding the ipaddress field. I've been trying several versions and sometimes the form creates a new user but the ipaddress is not saved..
The current code above gives me there error on POST:
DoesNotExist at / User matching query does not exist. Due to this line "user.refresh_from_db() # Load the profile instance created by the Signal"
From the docs:
This save() method accepts an optional commit keyword argument, which accepts either True or False. If you call save() with commit=False, then it will return an object that hasn’t yet been saved to the database.
So since you're passing commit in as False you're getting an unsaved instance back. Attempting to call refresh_from_db on an object that doesn't actually exist in the database will fail, as it is clearly doing. If the instance to a model has no id then refresh_from_db will fail when called on it.
As for the continuing inability to save IP address, I noticed that your form meta has the model set to the User object. The default Django User object has no ip address. I see that in the model file you linked you have a Profile model that does have an IP Address so in that case I think you simply have your form set up wrong. Or you need to handle the request differently.
Form change
Currently your form is attempting to create/modify a Django User model. Unless you've made a custom User model that you didn't show, this user model will not have an ipaddress as a field in the database meaning even if you set user.ipaddress = <address> and then save the user, the ip address won't persist outside of the current scope since all you did was declare a new variable for the user instance.
If you change your form to point at your Profile model you'll be able to save the address using profile.ipaddress = <address> and save it successfully. But you will have to update your template since by default it will only show the fields for your profile and not the user object associated with it.
Change Template/View
You can also change the template and view to accommodate it. Apparently your view is able to produce an IP Address using the get_ip function so for the time being I'll assume your template is fine as is so the only changes that need to be made are to your view.
Currently your view is getting an unsaved User instance back when it calls form.save. This means you need to save the user and then create a Profile model that references it with your ip address attached.
def index(request):
#request.session.flush()
if request.user.is_authenticated:
return redirect('ve:dashboard')
elif request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
user = form.save(commit=False)
# do anything you need to the unsaved user here
user.save()
prof = Profile.objects.create(user=user,
ipaddress=get_ip(request),
date=form.cleaned_data.get('birth_date')
# no need to save prof since we called objects.create
raw_password = form.cleaned_data.get('password1')
user = authenticate(username=user.username, password=raw_password)
login(request, user)
return redirect('ve:dashboard')
else:
form = RegistrationForm()
return render(request, 'index.html', {'form': form})
Python noob here trying to learn something very simple.
I'm trying to create a basic form that takes some personal information from a user and saves it into a sqlite3 table with the username of the user as the primary key.
My models.py looks like this:
class userinfo(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, primary_key= True,
on_delete=models.CASCADE)
name = models.CharField(max_length = 200, blank = True)
email = models.EmailField(max_length= 300, default = 'Null')
phone = models.CharField(max_length= 10, default = 'Null')
def __unicode__(self):
return self.user
forms.py:
class NewList(forms.ModelForm):
class Meta:
model = userinfo
exclude = {'user'}
views.py
def newlist(request):
if request.method == 'POST':
form = NewList(request.POST)
if form.is_valid():
Event = form.save(commit = False)
Event.save()
return redirect('/home')
else:
form = NewList()
return render(request, 'home/newlist.html', {'form': form})
html:
{% load static %}
<form action="/home/" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit">
</form>
urls.py too, but I don't know how that would help:
urlpatterns = [
url(r'^newlist/$', views.newlist, name='newlist')
]
So when I go to the url, I see the form. I can then fill the form, but when I submit the form, the data doesn't go into the database.
What am I doing wrong here?
Thanks in advance!
I think all you need to do is just save the form if it's valid, probably also add the userinfo as an instance of the form. You are also exluding the user from the form and need to assign it manually.
def newlist(request):
if request.user.is_authenticated():
user = request.user
if request.method == 'POST':
form = NewList(request.POST, instance=user.userinfo)
if form.is_valid():
form.save(commit=false)
form.user = user
form.save()
return redirect('/home')
else:
form = NewList(instance=user.userinfo) # add this if you want it to automatically fill the form with the old data if there is any.
return render(request, 'home/newlist.html', {'form': form})
The rest look like it should work.Except you need to send the post URL back to newlist:
{% load static %}
<form action="/newlist/" method="POST">
{% csrf_token %}
{{ form.as_p }}
</form>
If users are assigned at the creation of the model the first time, you don't need the user save, but since this is saving a users data you want to make sure they are logged in anyway:
def newlist(request):
if request.user.is_authenticated():
user = request.user
if request.method == 'POST':
form = NewList(request.POST, instance=user.userinfo)
if form.is_valid():
form.save()
return redirect('/home')
else:
form = NewList(instance=user.userinfo) # add this if you want it to automatically fill the form with the old data if there is any.
return render(request, 'home/newlist.html', {'form': form})
The instance is the model it is either going to save to, or copying data from. In the: form = NewList(request.POST, instance=user.userinfo) part, it is taking the POST data from the form, and linking that to the specific model entry of user.userinfo, however, it will only save to the database when you call form.save().
The user.userinfo is just so you can get the correct form to save to, as userinfo is a onetoone model to user. Thus making it possible to get it with user.userinfo.
The form = NewList(instance=user.userinfo) part is where it takes the currently logged in user's userinfo and copies into the form before it is rendered, so the old userinfo data will be prefilled into the form in the html. That being if it got the correct userinfo model.
I am pretty new to Django but have gone through a few tutorials and have taken it upon myself to create an app very similar to the Tango with Django walkthrough app, rango.
Everything has worked so far, but when I attempt to add a registration feature, the link brings me to the wrong template. I think this be because I have migrated parts from another app and so perhaps the webpage is looking for something that isn't there?
Here is my forms.py:
from django import forms
from django.contrib.auth.models import User
from address_book.models import Client, UserProfile
class ClientForm(forms.ModelForm):
name = forms.CharField(max_length=128, help_text="Name: ")
phone = forms.IntegerField(help_text="Phone Number: ")
address = forms.CharField(max_length=128, help_text="Address: ")
desired_weight = forms.IntegerField(help_text="Desired Weight: ")
start_weight = forms.IntegerField(help_text="Start Weight: ")
views = forms.IntegerField(widget=forms.HiddenInput(), initial=0)
likes = forms.IntegerField(widget=forms.HiddenInput(), initial=0)
slug = forms.CharField(widget=forms.HiddenInput(), required=False)
comments = forms.CharField(max_length=500, help_text="Comments: ")
# An inline class to provide additional information on the form.
class Meta:
# Provide an association between the ModelForm and a model
model = Client
fields = ('name', 'phone', 'address', 'desired_weight', 'start_weight',)
class UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput())
class Meta:
model = User
fields = ('username', 'email', 'password')
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('website', 'picture')
models.py
from django.db import models
from django.template.defaultfilters import slugify
from django.contrib.auth.models import User
class Client(models.Model):
name = models.CharField(max_length=128, unique=True)
phone = models.IntegerField(default=0)
desired_weight = models.IntegerField(default=0)
start_weight = models.IntegerField(default=0)
address = models.CharField(max_length=128, blank=True)
comments = models.CharField(max_length=500, blank=True)
slug = models.SlugField(unique=True)
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(Client, self).save(*args, **kwargs)
def __unicode__(self):
return self.name
class UserProfile(models.Model):
# This line is required. Links UserProfile to a User model instance.
user = models.OneToOneField(User)
# The additional attributes we wish to include.
website = models.URLField(blank=True)
picture = models.ImageField(upload_to='profile_images', blank=True)
# Override the __unicode__() method to return out something meaningful!
def __unicode__(self):
return self.user.username
views.py
from django.http import HttpResponse
from django.shortcuts import render
from address_book.forms import ClientForm, UserForm, UserProfileForm
from address_book.models import Client
def index(request):
client_list = Client.objects.all().order_by('name')
# Construct a dictionary to pass to the template engine as its context.
# Note the key boldmessage is the same as {{ boldmessage }} in the template!
context_dict = {'clients': client_list}
# Return a rendered response to send to the client.
# We make use of the shortcut function to make our lives easier.
# Note that the first parameter is the template we wish to use.
return render(request, 'address_book/index.html', context_dict)
def add_client(request):
# A HTTP POST?
if request.method == 'POST':
form = ClientForm(request.POST)
# Have we been provided with a valid form?
if form.is_valid():
# Save the new category to the database.
form.save(commit=True)
# Now call the index() view.
# The user will be shown the homepage.
return index(request)
else:
# The supplied form contained errors - just print them to the terminal.
print form.errors
else:
# If the request was not a POST, display the form to enter details.
form = ClientForm()
# Bad form (or form details), no form supplied...
# Render the form with error messages (if any).
return render(request, 'address_book/add_client.html', {'form': form})
def client(request, client_name_slug):
# Create a context dictionary which we can pass to the template rendering engine.
context_dict = {}
try:
# Can we find a category name slug with the given name?
# If we can't, the .get() method raises a DoesNotExist exception.
# So the .get() method returns one model instance or raises an exception.
client = Client.objects.get(slug=client_name_slug)
context_dict['client_name'] = client.name
context_dict['client_name_slug'] = client_name_slug
context_dict['client_phone'] = client.phone
context_dict['client_address'] = client.address
context_dict['desired_weight'] = client.desired_weight
context_dict['start_weight'] = client.start_weight
context_dict['comments'] = client.comments
# Retrieve all of the associated pages.
# Note that filter returns >= 1 model instance.
# pages = Page.objects.filter(category=category)
# Adds our results list to the template context under name pages.
# context_dict['pages'] = pages
# We also add the category object from the database to the context dictionary.
# We'll use this in the template to verify that the category exists.
context_dict['client'] = client
except Client.DoesNotExist:
# We get here if we didn't find the specified category.
# Don't do anything - the template displays the "no category" message for us.
pass
# Go render the response and return it to the client.
print context_dict
return render(request, 'address_book/client.html', context_dict)
def register(request):
# A boolean value for telling the template whether the registration was successful.
# Set to False initially. Code changes value to True when registration succeeds.
registered = False
# If it's a HTTP POST, we're interested in processing form data.
if request.method == 'POST':
# Attempt to grab information from the raw form information.
# Note that we make use of both UserForm and UserProfileForm.
user_form = UserForm(data=request.POST)
profile_form = UserProfileForm(data=request.POST)
# If the two forms are valid...
if user_form.is_valid() and profile_form.is_valid():
# Save the user's form data to the database.
user = user_form.save()
# Now we hash the password with the set_password method.
# Once hashed, we can update the user object.
user.set_password(user.password)
user.save()
# Now sort out the UserProfile instance.
# Since we need to set the user attribute ourselves, we set commit=False.
# This delays saving the model until we're ready to avoid integrity problems.
profile = profile_form.save(commit=False)
profile.user = user
# Did the user provide a profile picture?
# If so, we need to get it from the input form and put it in the UserProfile model.
if 'picture' in request.FILES:
profile.picture = request.FILES['picture']
# Now we save the UserProfile model instance.
profile.save()
# Update our variable to tell the template registration was successful.
registered = True
# Invalid form or forms - mistakes or something else?
# Print problems to the terminal.
# They'll also be shown to the user.
else:
print user_form.errors, profile_form.errors
# Not a HTTP POST, so we render our form using two ModelForm instances.
# These forms will be blank, ready for user input.
else:
user_form = UserForm()
profile_form = UserProfileForm()
# Render the template depending on the context.
return render(request,
'address_book/register.html',
{'user_form': user_form, 'profile_form': profile_form, 'registered': registered} )
register.html
{% extends 'base.html' %}
{% load staticfiles %}
{% block title %}Register{% endblock %}
{% block body_block %}
<h1>Register with 3010 Weightloss !</h1>
{% if registered %}
Return to the homepage.<br />
{% else %}
Rango says: <strong>register here!</strong><br />
<form id="user_form" method="post" action="/address_book/register/"
enctype="multipart/form-data">
{% csrf_token %}
<!-- Display each form. The as_p method wraps each element in a paragraph
(<p>) element. This ensures each element appears on a new line,
making everything look neater. -->
{{ user_form.as_p }}
{{ profile_form.as_p }}
<!-- Provide a button to click to submit the form. -->
<input type="submit" name="submit" value="Register" />
</form>
{% endif %}
{% endblock %}
urls.py
from django.conf.urls import patterns, url
from address_book import views
urlpatterns = patterns('',
url(r'^$', views.index, name='index'),
url(r'^add_client/$', views.add_client, name='add_client'),
url(r'^(?P<client_name_slug>[\w\-]+)/$', views.client, name='client'),
url(r'^register/$', views.register, name = 'register'),
)
index.html
{% extends 'base.html' %}
{% load staticfiles %}
{% block title %}Index{% endblock %}
{% block body_block %}
<head>
<title>Rango</title>
</head>
<body>
<h2>Current Clients:</h2>
{% for client in clients %}
<li>{{ client.name }}</li>
{% endfor %}
</body>
{% endblock %}
and finally my base.html:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>Address_book</title>
</head>
<body>
{% block body_block %}{% endblock %}
</body>
<h2> Need to make changes? </h2>
<ul>
<li>Add new client</li>
<li>Register here</li>
</ul>
</html>
Like I said above, when I click the link to register in index.py, it brings me to another template, client.html.
It looks like the problem is that /address_book/register/ matches your client url, which comes before the register URL. To fix that, one way to fix this would be to switch the order of the URL strings:
# urls.py
urlpatterns = patterns('',
url(r'^$', views.index, name='index'),
url(r'^add_client/$', views.add_client, name='add_client'),
url(r'^register/$', views.register, name = 'register'),
url(r'^(?P<client_name_slug>[\w\-]+)/$', views.client, name='client'),
)
However, it would be better to call the URL by its name in your base.html instead of relying on the order of the URL strings:
# base.html
...
<li>Register here</li>
...
I've already enabled the ability for users to create profiles; however, I need to be able to save additional data to the profile once the user has already logged in.
Say that John is logged in. After he logs in, he decides that he wants to bookmark a certain term. Every time that he bookmarks a term (the url of the current page), it will be added to the "terms" section associated with his profile.
I'm not sure how to allow for the addition of more than one term - don't think CharField is correct, and I also don't know how to link the form in the template to the view so that it actually saves this data to the profile. Here is what I currently have:
models.py
class UserProfile(models.Model):
user = models.OneToOneField(User)
terms = models.CharField(max_length=1000)
views.py
This is how I created my user:
def register(request):
context = RequestContext(request)
registered = False
if request.method == 'POST':
user_form = UserForm(data=request.POST)
profile_form = UserProfileForm(data=request.POST)
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save()
user.set_password(user.password)
user.save()
profile = profile_form.save(commit=False)
profile.user = user
profile.save()
registered = True
else:
print user_form.errors, profile_form.errors
else:
user_form = UserForm()
profile_form = UserProfileForm()
return render_to_response(
'app/register.html',
{'user_form': user_form, 'profile_form': profile_form, 'registered': registered},
context)
And this is what I'm attempting to do with terms:
def terms(request):
context = RequestContext(request)
url = request.get_full_path()
profile = UserProfile()
profile.topics = url
profile.save()
return render_to_response('app/page1.html', {}, context)
urls.py
url(r'^terms/$', views.terms, name='terms'),
And the template file.
<form method="post" action="{% url 'terms' %}" enctype="multipart/form-data">
{% csrf_token %}
<input type="submit" name="submit" value="Save" />
</form>
I'm think you can use https://github.com/brosner/django-bookmarks to save bookmarks for any user without your code.
Or add model smt about Term like this
class Term(models.Model):
user = models.ManyToManyField(User, related_name='terms')
term = models.CharField(max_length=1000)
and use user.terms.all() for get all user's terms.
But, if you need save your schema and models, you need custom field which will works like array field in postgres. If you use postgresql db, add https://github.com/ecometrica/django-dbarray to you project and use terms like TextArrayField, if you db is not postgres see here -
What is the most efficent way to store a list in the Django models?
second answer titled "Premature optimization is the root of all evil." by user jb.
Solution with relations is "normalized" - simple and strong
Solution with "array-field" is "demormalized" - faster but with risks of non-consistently data.