I was able to create a schema (and I confirmed this via database) but for some reason, I am getting a Can't create tenant outside the public schema. Current schema is error when creating schema and then also I am getting this error No tenant for hostname when I try to visit the tenants domain and I am not sure what is causing it. Below is my code:
views.py
def post(self, request):
form = CreatePortalForm(request.POST)
if form.is_valid():
getDomain = form.cleaned_data.get('name')
instance = form.save(commit=False)
user_id = request.user.id
user = User.objects.get(id=user_id)
tenant = Client(schema_name=getDomain, name=getDomain, created_by=user)
tenant.save()
domain = Domain()
domain.domain = getDomain + ".example.com:8000"
domain.tenant = tenant
domain.is_primary = True
domain.save()
with schema_context(tenant.schema_name):
instance.save()
redirect = 'http://' + getDomain + '.example.com:8000'
return HttpResponseRedirect(redirect)
return render(request, "registraton/create_portal.html", {"form": form})
For example, I have created three schemas:
tenant1
tenant2
tenant3
All three tenants have created the tables in the database, but I get the Can't create tenant outside the public schema. Current schema is error when running the above script to create the schema and domain or I get the No tenant for hostname when trying to visit the tenants domain.
Like I said, the schema is creating and migrating successfully but I still cannot get to the domain.example.com as it throws the No tenant for hostname error. Any ideas on what is causing this?
The django-tenants docs says that you shouldn't specify the port for your domain.
See this here
Related
I have created a Django application, where I ask the visitor of the site to input their name into a form. Upon POST submission, it stores their name, and the IP address they are visiting from into an SQLite3 database. For practise, I encrypt the IP address before storing it in the db.
If the user "returns" to the site, my code is set to find a record for that user in the db first, based on their IP address. If there's already a record in the db with that IP, it'll display a "welcome back" message.
If I don't encrypt the IP, my app works fine, but when I attempt to use django_cryptography I get stuck.
What I have so far is:
models.py
from django.db import models
from django import forms
from django_cryptography.fields import encrypt
INT_MAX_NAME_STRING_LENGTH = 100
# Previous visitors object
class Previous_Visitor_Model(models.Model):
visitor_name = models.CharField(max_length=INT_MAX_NAME_STRING_LENGTH)
visitor_ip = encrypt(models.CharField(max_length=16)) # IP_Address_Field()
# The name form on the initial page
class NameForm(forms.Form):
visitor_name = forms.CharField(label='Your (company) name', max_length=INT_MAX_NAME_STRING_LENGTH)
So I encrypt their IP address in the model. When a visitor enters their name into the form and submits, a new record is created in the db, with their name, and BLOB as the encrypted IP address value. If I don't use encryption here, it stores something like 127.0.0.1 instead, which it should.
So now, as you'll see in the views.py below, I attempt to get() a record from the db by current_visitors_ip. Again, without encryption this works fine, but since I've encrypted the IP's it doesn't find the records. My guess is because I'm now trying to compare a string value of 127.0.0.1 as the current_visitors_ip variable, to the BLOB values that it's getting from the db, so I have also tried throwing encrypt() around the current_visitors_ip variable as well thinking those two values SHOULD be the same, but it's still not working.
There's a great article here about how to allow for encrypting/decrypting values while querying the db, but this involves using separate encrypt/decrypt functions which isn't the end of the world, but I like the idea of using django's cryptography library for this. Ideally if it could DECRYPT the values it's get()ing from the db, that would be ideal, but I don't think they have such a function.
I know this is a slow way of doing things, I'm just wanting to practise. Here is the views.py logic.
views.py
from django.shortcuts import render
from .models import Previous_Visitor_Model, NameForm
from django.http import HttpResponseRedirect
from django_cryptography.fields import encrypt
# Helper Function to get the current visitor's IP address
def get_client_ip(request):
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
else:
ip = request.META.get('REMOTE_ADDR')
ip = str(ip)
return ip
# Create your views here.
# Initial visit logic
def initial_visit_view(request):
# Get the current visitor's IP Address
current_visitors_ip = get_client_ip(request)
# If the current visitor has never been here before, based on their ip ...
try:
existing_user = Previous_Visitor_Model.objects.get(visitor_ip=current_visitors_ip)
except:
existing_user = None
# Create a new record in the db for this user's name and IP
if existing_user == None:
if request.method == 'POST':
# Create a form instance and populate it with data from the request:
form = NameForm(request.POST)
if form.is_valid():
# Save the user's data (upon POST of name)
Previous_Visitor_Model(visitor_name=form.cleaned_data['visitor_name'], visitor_ip=current_visitors_ip).save()
# redirect to a new URL:
return HttpResponseRedirect('/projects/')
# if a GET (or any other method) we'll create a blank form
else:
form = NameForm()
context = {'form': form}
return render(request, 'first_visit.html', context)
# Else if they're a repeat visitor
elif existing_user != None:
context = {
'current_visitor': existing_user
}
return render(request, 'repeat_visit.html', context)
UPDATE
I suppose a better way would be to just hash the IP addresses before storing them in the db, and just compare the hashes as described here, but just wondering if there's an answer to my question. Thanks!
Summary: (Python+Flask+MongoDB) Defined two models - User and Post. Post references User. Trying to create a Post by referencing a saved User but getting You can only reference documents once they have been saved to the database error.
Detail
Hello! I'm following a python (installed v3.7), flask (installed v1.1.2), MongoDB (installed mongoengine v0.23.0) tutorial to write a simple blogging app.
I have a User model and a Post model. A User may register, login and write Posts. I am able to create and save Users. However, when I try to create a Post by referencing a (previously created & saved) User, I get a Mongoengine ValidationError You can only reference documents once they have been saved to the database
The User object referenced in the Post object is present in the DB - confirmed that via the Mongo shell.
Any pointers/help/inputs would be much appreciated. I am new to MongoDB.
Code
# models.py
from flask_mongoengine import MongoEngine
db = MongoEngine()
class User(db.Document):
_id = db.ObjectIdField()
username = db.StringField(required = True)
email = db.EmailField(required = True)
class Post(db.Document):
_id = db.ObjectIdField()
created_by = db.ReferenceField(User)
title = db.StringField(required = True)
body = db.StringField(required = True)
# blog.py
""" Module that creates blog posts
from app.models import User, Post
def create():
if request.method == 'POST':
title = request.form['title']
body = request.form['body']
user = User.objects.get(_id=g.user['_id'])
# check user fetched by printing object id
# user[_id] matches the User ObjectID obtained via the mongo shell
print('user id = ' + str(user['_id']))
post = Post(title=title, body=body)
post.created_by = user
post.save() # --> this line generates the error
return redirect(url_for('blog.index'))
return render_template('blog/create.html')
Error
File "/Users/<my_username>/.pyenv/versions/3.7.3/lib/python3.7/site-packages/mongoengine/base/document.py", line 433, in validate
raise ValidationError(message, errors=errors)
mongoengine.errors.ValidationError: ValidationError (Post:None) (You can only reference documents once they have been saved to the database: ['created_by'])
Based on this Stackoverflow question, I was able to make this work by saving the User and then passing the DBRef of the User into the Post.
user = User.objects.get(_id=g.user['_id'])
user.save()
post = Post(title=title, body=body)
post.created_by = user.to_dbref()
post.save()
I have an application where users are authenticated using active directory. This means that there is no need to store passwords in our application database. We store each users details such as username etc in a users table in our application database.
I am working on implementing functionality to allow users to be added to the application. I am using a ModelForm to generate the form.
The model form looks like this:
class GeminiUserAccessForm(ModelForm):
class Meta:
model = GeminiUser
fields = ['email', 'is_authorised']
labels = {
'is_authorised': 'Is authorised?'
}
The method in the view to handle requests from the form page:
def manage_user_access_view(request):
template_name = 'gemini_users/manage_access.html'
if request.method == 'POST':
form = GeminiUserAccessForm(request.POST)
if form.is_valid():
new_user = form.save()
pass
else:
form = GeminiUserAccessForm()
return render(request, template_name, {'form': form})
Everything works as expected, a user with the given email and is authorised value is saved to the database. The password is left blank.
Do I also need to use set_unusable_password() before saving the user?
It'd be good form and security-in-depth, just in case someone enables authentication that looks at the password field for one reason or another.
set_unusable_password() calls make_password(None), which returns
UNUSABLE_PASSWORD_PREFIX + get_random_string(UNUSABLE_PASSWORD_SUFFIX_LENGTH)
when password is None, i.e. a password hash that is never considered valid by any of the out-of-the-box hash algorithms.
When trying to access the database, I am unable to access the column. I have run the init database script multiple times. Here's the code:
View
from .models import Session, Admin
#view_config(route_name='admin', renderer='templates/admin.pt')
def admin(request):
session = Session()
user = authenticated_userid(request)
admin = bool(session.query(Admin).filter_by(username = user).first())
message = ''
if admin:
if 'form.submitted' in request.params:
session.add(Admin(request.params['admin']))
session.commit()
message = "%s has been added as an Admin!" % request.params['admin']
return dict(url=request.route_url('admin'),
message = message,
)
return dict(message = message)
else:
return HTTPForbidden()
Models
class Admin(Base):
__tablename__='admins'
__table_args__={
'mysql_engine':'InnoDB',
'mysql_charset':'utf8',
}
username = Column(String(255), ForeignKey('users.username'), primary_key=True)
def __init(self, user):
self.username = user
The database script can't add column on an already existing table. If you modified or added the column after running the script, that's where your problem come from.
You have three possibilities :
Delete the database and let the script recreate it the right way
Run the sql to add the column by hand.
Take a look at Alembic, which can help you do that automatically.
I am setting up a payment gateway and am using sessions to store data across page requests. The class below is used for organizing and storing information to the session.
class Gateway:
def __init__(self, session_key=None, session_name="FOO"):
# Store session ID and name
self.session_key = session_key
self.session_name = session_name
# Get the session
session = SessionStore(session_key=self.session_key)
try :
data = session[self.session_name]
except :
data = {user_id:None, checked_in:False }
self.__dict__.update(data)
def save(self) :
session = SessionStore(session_key=self.session_key)
session[self.session_name] = deepcopy(self.__dict__)
try :
del session['session_key']
del session['session_name']
except :
pass
session.save()
This view checks to see if the user is logged in. If he/she is, then he/she is redirected. If not, he/she is asked to either login or check in as a guest.
def check_in(request):
gateway = Gateway(session_key=request.session.session_key)
if request.user.is_authenticated():
gateway.user_id = request.user.id
gateway.checked_in = True
gateway.save()
return redirect('next_step')
else:
login_form = FormLogin()
if request.POST:
data = request.POST.copy()
if 'login' in data:
login_form = FormLogin(data)
if login_form.is_valid():
user = login(request, login_form)
if user:
gateway.user_id = user.id
gateway.checked_in = True
gateway.save()
return redirect('next_step')
elif 'guest' in data:
gateway.checked_in = True
gateway.save()
return redirect('next_step')
return render(
request,
'shop/login.html',
{
'login_form':login_form,
}
)
The next view checks the "checked_in" variable. This is to make sure that users are not skipping over the login/checkin process. (As a side note, the function "login(request, login_form)" is a function that is works perfectly in other contexts and returns the User if it was successful and None otherwise)
def next_step(request):
gateway = Gateway(session_key=request.session.session_key)
if not gateway.checked_in:#edited
messages.info(request, _(u'You must specify login first.'))
return redirect('check_in')
else:
#do the next step
Now for the problem:
Even when the user is authenticated, the "checked_in" variable is still false and causes the views to loop. A new session with a new session id is created each time that that I set the variable and save. The django docs have some explanation about the modification of sessions, but I cannot understand why new session is being created or why the session key is changing.
edit:
I am using the database backend.
I have duplicated this bug/issue:
URL RULE
url(r'^test/', 'shop.views.catalog.test', name="test")
VIEW FUNCTION
def test(request) :
key1 = request.session.session_key
request.session['test'] = 'test'
key2 = request.session.session_key
raise Exception("%s : %s === %s" % (key1, key2, request.session['test']))
Clear cookies for 127.0.0.1
go to 127.0.0.1:8000/test/
Exception at /test/
4793f2453758d7021a43a348a0f40a83 : 8568f729991e740395179c56cd37cf18 === test
refresh the page (w/o clearing cookies)
Exception at /test/
8568f729991e740395179c56cd37cf18 : 8568f729991e740395179c56cd37cf18 === test
so until the first time my session is modified, I have a different session key... unexpected behavior. I'm also curious why.
Django will not persist a session to the database if it has not been accessed or modified, so I believe the session_key you are using to initialise SessionStore is not actually backed by a database entry.
If this is the case: when you save your SessionStore it will be allocated a new session_key automatically [1] (as the existing key does not exist in the DB and we want to avoid session fixation [2]) and saved to the DB but the client will not be allocated this new session_key because your SessionStore is independent of request.session (which remains unmodified).
[1] https://github.com/django/django/blob/master/django/contrib/sessions/backends/db.py#L22
[2] https://groups.google.com/forum/?fromgroups#!topic/django-users/8b_6oTaXv7Q
The simple fix to test this hypothesis out would be to set request.session['kate'] = 'bob' before you initialise your Gateway class as this should force request.session to be persisted. You might like to refactor your Gateway class so that methods that need access to the session take request.session as an argument.
Check your SESSION_COOKIE_SECURE value and make sure you are using HTTPS when True..
https://github.com/lepture/flask-wtf/issues/76