404 error on unique page creation with google app engine - python

I asked a similar question here: create permenant unique links based on a user ID but couldn't quite get an answer. I am trying to give every user on my site a unique profile page. I have it mostly set up but I keep getting a 404 error. Now I am not sure if it is a problem with my handler or just the whole way I am doing it.
Here is my app code:
app = webapp2.WSGIApplication([('/', MainPage),
(r'/profile/(.+)', ProfilePage)])
and here is my ProfilePage handler class:
class ProfilePage(webapp2.RequestHandler):
def get(self, profile_id):
profileowner = User.get_by_id(profile_id)
if profileowner:
#Get all posts for that user and render....
#theid is their federated_id
theid = profileowner.theid
personalposts = db.GqlQuery("select * from Post where theid =:1 order by created desc limit 30", theid)
#I collect this so that I can have their username in the top of the page
global visits
logout = users.create_logout_url(self.request.uri)
currentuser = users.get_current_user()
self.render('profile.html', user = currentuser, visits = visits, logout=logout, personalposts=personalposts)
else:
self.redirect("/")
For an ID of 1201, which I found in the datastore viewer for a use, I have been testing it by typing in www.url.com/profile/1201 and that is when I get the 404 error.
Update:
It now is redirecting me to the main page with Amber's suggested change.
Now when I change this line:
profileowner = User.get_by_id(profile_id)
to this:
profileowner = User.get_by_id(17001)
it goes through correctly so I am guessing that that line is not correctly getting the profile_id from the URL

r'/profile/<profile_id>'
is not a valid regular expression. You probably want something like this instead:
r'/profile/(.+)'

Related

Django: forbid to add the same data for user

I have app where user can add url to favorite in database with ManyToMany relationship. So every user can have a lot of urls and every url can have a lot of users. I have problem with creating restriction to avoid adding the same url for user. I mean I want to create the mechanism where url can be added a lot of time for users, but only one per user.
In my models I have:
from django.db import models
from django.contrib.auth.models import User
class Repository(models.Model):
url = models.CharField(max_length=250,unique=False)
user = models.ForeignKey(User,related_name='user',on_delete=models.CASCADE,default='')
repositorys = models.ManyToManyField(User,related_name='user')
And in my views:
def favorites(request):
url = request.session.get('url')
repo = Repository(url=url,user=request.user)
repo.save()
repo.repositorys.add(request.user)
user = User.objects.get(username=request.user.username)
repos = user.users.all()
return render(request, 'favorites.html',{'url':url,'repos':repos})
Favorites function is called on clicked button in my template. When I click the above function is executed and redirect to /favorites. The problem is, when I click again on button and Im logged as the same user, this url is added again to database. Same problem with refreshing favorites.html. Is there any logic way to solve this problem?
EDIT:
view favorites:
def favorites(request):
url = request.session.get('url')
try:
repo = Repository(url=url,user=request.user)
repo.save()
repo.repositorys.add(request.user)
user = User.objects.get(username=request.user.username)
except IntegrityError as e:
return render_to_response('base.html')
repos = user.users.all()
return render(request, 'favorites.html', {'url': url, 'repos': repos})
My idea here was to return all urls for user then move adding to try block. When there is a IntegrityError then move to base.html and later will display some messages
Use unique_together in your model to manage uniqueness at the database level
class Repository(models.Model):
url = models.CharField(max_length=250,unique=False)
user = models.ForeignKey(User,related_name='user',on_delete=models.CASCADE,default='')
class Meta:
unique_together = [['url', 'user']]
then when you want to add a URL for a specific user, handle unique error to prevent a user to add a specific URL twice or more.
try:
# add a Repository
except IntegrityError:
# raise an error

Redirect in flask does not actually redirect to the desired page.

class XX(MethodView):
def get(self):
....
def post(self):
error = None
user = request.form['username']
password = request.form['password']
print user,password
if user == 'xxx' and password == 'xx':
session['logged_in'] = True
session['session_user'] = 'xx'
return redirect("control")
else:
errors = []
errors.append("Login error")
return render_template("login.html" , errors=errors)
#adding rule for control Blueprint
control.add_url_rule("/control",view_func= ControlView.as_view('control'))
The code snippet checks for specific username,password and should redirect to a specific page.
When requested for the login page, a GET request is sent.The above snippet gets username and password and adds to session dictionary.The redirect method results in a POST request and response code is 302 FOUND followed by a GET request to desired page with 200 OK response code.
But the redirection does not happen, it remains on same login page.
what should redirect have as parameters?
redirect('control')
redirect(url_for('control'))
redirect(url_for('control.control'))
Why using render_template('XX.html') responds with 500 response code ??
Is this code inside of a blueprint named 'control'? I'm guessing yes, based on the code shown above.
When you're using blueprints, you cannot know the final URL (eg: /control) in advance, since the blueprint user can map the base URL anywhere they'd like.
The best way to redirect to your view, assuming this is a blueprint, is using the blueprint URL notation: redirect(url_for('control.control')).
If this is NOT going to be used in a blueprint, you should be able to do something like: redirect(url_for('control')) so long as that's your view name when added to the route.
Hope this was clear.
If you're still having errors when trying to two above examples, enable Flask debugging (app.config['DEBUG'] = True) and re-try. The traceback should explain what's happening in more depth.

css working on all but two pages

Specifically, pages using an optional regular expression. By optional, I mean PAGE_RE below.
I am creating a Wiki. If a user searches a term, and that term doesn't already exist, then the user is redirected to an edit page so they can create the new content. This only happens, however, if the user is logged in. To determine if the user is logged in, I check for a cookie. If the user isn't logged in(no cookie), then I redirect, not to the edit page, to create the new content, but to the login page, dealt with by the Login class below. The user logs in, a cookie is created, and then they are redirected to the edit page to create the content they originally searched for. In order to remember what their topic was, so I can redirect after the login, I send the topic(in the form '/topic') to the Login class, where it's received as a parameter by the get and post methods. If a user just comes to the site and logs in normally, they are redirected to the home page, but in this case, because the topic has been received by get and post and is not None, I use the line self.redirect('/edit/%s' % topic[1:]) below, to send them on to their original destination. The problem is, css isn't working for the two urls in below that use PAGE_RE. JsFiddler4 shows that there is a 404 involving /login/css/wiki.css. It suddenly clicked after some time that that url is not the url for the login page when it receives the extra 'topic' param. It is also the case with EditPage. How can I get css to work on these pages/urls when they are sometimes different? I didn't know what was going on for ages, then I downloaded and ran JsFiddler4 and figured it out. I am using Google App Engine, webapp2, jinja2. Any help much appreciated. Apart from those two pages, css works fine.
This code is out of order and incomplete, but I hope it's sufficient
PAGE_RE = r'(/?(?:[A-Za-z0-9_-]+/?)*)'
app = webapp2.WSGIApplication([
('/signup', Register),
('/logout', Logout),
('/login' + PAGE_RE, Login),
('/edit' + PAGE_RE, EditPage),
('/', Front),
(PAGE_RE, WikiPage),
], debug=True)
class Login(Main):
""" Validate form and validate users cookie """
def get(self, topic):
self.render('login.html', error={})
def post(self, topic):
username = self.request.get('username')
password = self.request.get('password')
if not username or not self.valid(user=username):
self.login_error(user=username)
elif not password or not self.valid(pw=password):
self.login_error(user=username)
elif not self.user_exists(username):
self.render('login.html', error={'no_user':'That user does not exist'})
else:
self.login(username, password, topic=topic)
def login(self, name, pw, topic):
user_hash = User.get_user_hash(name)
if self.valid_pw(name, pw, user_hash):
self.create_secure_cookie('user_id', name)
if topic:
self.redirect('/edit/%s' % topic[1:])
else:
self.redirect('/')
else:
self.login_error(user=name)
def login_error(self, user):
self.render('login.html', username=user, error={'login': errors['login']})
def valid_pw(self, name, pw, user_hash):
salt = user_hash.split('|')[0]
return user_hash == self.create_user_hash(name, pw, salt)
Ok, I solved this. Here's what appeared to be happening.
This redirect was sending a parameter to the Login class above:
self.redirect('login/%s' % wiki_topic) #wiki_topic = something like 'topic'
when looking for the css for a page, what seems to happen is that the last part of the path up to the '/' is taken off, and replaced by the path to the css, '/css/wiki.css' in my case.
So I was passing 'login/topic' and just 'topic' was being replaced by the css path to give:
'login/css/wiki.css' instead of the correct just 'css/wiki/css'.
To stop this from happening, I changed the line redirecting to the Login class from:
self.redirect('login/%s' % wiki_topic) to >> self.redirect('login%s' % wiki_topic)
the second version has no slash before the %s.

Unit testing Flask function (with logged in user)

I'm new to Python and Flask but I'm gradually getting to grips with it. I've got so far into building an app and I'm now thinking I should start some unit testing. I really can't get my head around it though. I've read various docs, posts and examples but I can't seem to transfer this to my own code. I'm hoping if someone can show me how to write a test for one of my functions then things will fall into place. It's very tempting at this stage to ignore it and press on building my app.
#app.route('/user/<nickname>/create_bike', methods = ['GET', 'POST'] )
#login_required
def create_bike(nickname):
user = User.query.filter_by(nickname = nickname).first()
bikes = user.bikes.all()
bikecount = user.bikes.count()
form = CreateBike()
if form.validate_on_submit():
if bikecount < user.bike_allowance:
# let user create a bike
newbike = Bikes(bikename = form.bikename.data,
user_id = g.user.id, shared = SHARED )
db.session.add(newbike)
db.session.commit()
flash('Your new bike has been created')
return redirect(url_for('create_bike', nickname = nickname))
else:
flash("You have too many bikes")
return render_template('create_bike.html',
user = user,
form = form
)
UPDATE - Here's my working test
def test_create_bike(self):
u = User(nickname = 'john', email = 'john#example.com', account_type = "tester")
db.session.add(u)
db.session.commit()
# login user
with self.app as c:
with c.session_transaction() as sess:
sess['user_id'] = int(u.get_id())
# http://pythonhosted.org/Flask-Login/#fresh-logins
sess['_fresh'] = True
rv = c.post('/user/john/create_bike', data = dict(
bikename = 'your new bike',
user_id = sess['user_id'],
shared = 1
), follow_redirects = True)
assert 'Your new bike has been created' in rv.data
Your test is likely failing because that view requires a logged in user, and since you arent passing in any session data, you are being redirected to the login page (the data argument to .post is form data, available in request.form in your view). Before your assertion you can see what the response is to help you along the way:
print rv.status_code
print rv.location #None if not a redirect
There's some documentation around sessions in tests here, and if you're using Flask-Login like it looks you are, this answer shows you how to set the session up so you get a logged in user
You're on the right track.
Checking if the html page contains some piece of data that should be there is one the most common scenarios in testing web apps. You just need to repeat for all the other pages that you create. You can also add tests to see that creating a user follows all the validation rules that you've setup or that the username field is unique etc.

Adding usernames to the path of the url

I am building an application using webapp2 in Google App Engine. How do I pass the username into the url so that when the profile button is clicked, it takes the user to "/profile/username" where "username" is specific to the user?
My current handlers:
app = webapp2.WSGIApplication([('/', MainPage),
('/signup', Register),
('/login', Login),
('/logout', Logout),
('/profile', Profile)
],
debug=True)
the Profile class:
class Profile(BlogHandler):
def get(self):
email = self.request.get('email')
product = self.request.get('product')
product_list = db.GqlQuery("SELECT * FROM Post ORDER BY created DESC LIMIT 10")
self.render('profile.html', email = email, product = product, product_list = product_list)
I am trying to send each user to a Profile page that contains information in my database specific to them. Thanks
One possible solution would be to simply have one URL, i.e., /profile. The corresponding handler would render the response with data coming from the logged-in user.
If you really want to have URLs like /profile/username, you could define a route:
app = webapp2.WSGIApplication([('/', MainPage),
('/signup', Register),
('/login', Login),
('/logout', Logout),
('r/profile/(\w+)', Profile)
],
debug=True)
and access the username in your handler:
class Profile(BlogHandler):
def get(self, username):
But depending on your application, you might want to make sure only the logged-in user has access to its /profile/username by adding a check somewhere in the handler.
See http://webapp-improved.appspot.com/guide/routing.html
You could have something like
class Profile(BlogHandler):
def get(self, username):
...
app = webapp2.WSGIApplication([('/profile/(\w+)', Profile), ...])
Start by adding a capture group to /profile:
(r'/profile/(\w+)', Profile)
The r before the beginning of the string is important, as it will correctly handle regular expression characters. Otherwise, you'd have to escape the blackslash manually.
\w+ will match one or more alphanumeric characters and the underscore. That should suffice for your usernames, yes?
Then set up your RequestHandler like this:
class Profile(webapp2.RequestHandler):
def get(self, username):
# The value captured in the (\w+) part of the URL will automatically
# be passed in to the username parameter.
# Do the rest of my coding to get and render the data.

Categories