I'm using (and learning) Django on my newest product, and I just installed and started using django-registration app to allow my users to auto-register themselves.
Everything's working good, but I'm thinking about assigning every new user to a specific group (already created), so I can "separate" the admins from the normal users.
How can I do this? The documentation about this app is awful, IMO.
I didn't create any model or configuration to start using the django-registration app.
I ended up making a fork for this, as this was driving me crazy.
https://github.com/kcunning/django-registration
Basically, add the following to your settings.py file:
REGISTRATION_DEFAULT_GROUP_NAME="Groupname"
Any user added through the registration module will be given that group upon creation.
To start, I'm not an expert at d-r (django-registration) and actually only checked it out because you mentioned it in your question. So, if anyone else has a better answer please submit it. Anyway, here it goes.
I noticed that the way users (or "profiles" as d-r calls them) are created is found in the create_inactive_user function located in registration.models.RegistrationManager. So, in this function after:
new_user.is_active = False
new_user.save()
You could add something like:
default_group = Group.objects.get(name=WHATEVER_THE_GROUP_NAME_IS)
user_group, is_created = UserGroup.get_or_create(user=new_user)
user_group.group = default_group
#user_group.groups.add(default_group) # Django ver. 1.7+
user_group.save()
You could probably do without the get_or_create as I don't think you can create more than one user with the same username, but I would rather be safe than sorry.
This method will cause you to edit the d-r files (which may not be the best thing to do) along with hardcoding certain things (such as the default group name), but it should be a good stepping off point.
PS. the default group name could be a variable located in the settings.py file, which would make it more accessible and pretty. Example:
settings.py:
DEFAULT_GROUP_NAME = 'YOUR GROUP NAME'
registration/models.py:
default_group = Group.objects.get(name=settings.DEFAULT_GROUP_NAME)
Anyway, hope that helps.
django-registration-redux has custom signals that can be used to assign registered user to a specific group. "user_registered" signal can be used for this purpose. Please refer to django-registration-redux documentation
from django.contrib.auth.models import Group
def user_created(sender, user, request, **kwargs):
group = Group.objects.get(name='Kurum')
group.user_set.add(user)
user.save()
from registration.signals import user_registered
user_registered.connect(user_created)
Related
I've got a project with quite a few admin-actions. Currently I'm registering them like so:
#admin.action(description='Some admin action description')
def do_something_action(self, request, queryset):
pass
Some of these are being added to the admin-class of another app so I cannot simply add the function directly on the class where they are needed.
The problem is that these actions are shown project-wide, on every admin-screen.
How can I stop this behaviour, and manually set them where they are wanted? If it matters, it's Django3.2.
As I couldn't find out why these actions are shown project wide, I decided to manually override the get_actions function.
Firstly, a Mixin was created to deal with the exclusion of certain actions.
class ExcludedActionsMixin:
'''
Exclude admin-actions. On the admin, you're expected to have
excluded_actions = [...]
Keep in mind that this breaks the auto-discovery of actions.
You will need to set the ones you actually want, manually.
'''
def get_actions(self, request):
# We want to exclude some actions from this admin. Django seems to auto assign all general actions
# that aren't included in the class by default to the entire package. But we have some actions
# intended for another package here. This wouldn't work.
actions = super().get_actions(request)
# so let's recompile the actions list and keeping excluded_actions in mind.
for excluded_action in self.excluded_actions:
try:
del actions[excluded_action]
except KeyError:
pass
return actions
This Mixin is used to do both local overrides in specific apps, but also to create a 'default' admin which contains the most wanted
class DefaultAdminActions(ExcludedActionsMixin, admin.ModelAdmin):
# There are a number of actions we want to be excluded pretty much everywhere. Instead of
# setting them again and again, we'll just delcare them here.
# And import DefaultAdmin instead of admin.ModelAdmin
excluded_actions = ['unwanted_action1', 'unwanted_action2', 'unwanted_action3']
Other approaches are more than welcome.
I would like to follow this guideline and avoid a nullable ForeignKey.
I have a ForeignKey to the django User model.
If I try to store request.user in an instance of this model, I get this error:
ValueError: Cannot assign "<SimpleLazyObject: <django.contrib.auth.models.AnonymousUser>>":
"MyModel.user" must be a "User" instance.
I think it is feasible to avoid the non-conditionless data schema (nullable foreign key).
How could I solve this?
Those guidelines suggest you create an instance of user that reflects an anonymous user. Otherwise known as a sentinel value. You'd have to keep track of it via some unique key, likely the username. Then make sure it exists and nobody else has actually created a user with that key otherwise you run into other problems.
However, because of those outlined issues above I disagree with those guidelines. If your data model allows for optional relationships, then you absolutely should use NULL values.
Regarding the comment:
If there is no NULL in your data, then there will be no NullPointerException in your source code while processing the data :-)
Simply because there are no NULL fields, doesn't mean those conditions don't exist. You still are handling these edge cases, but changing the names and some of the syntax. You're still vulnerable to bugs because you still have as many conditions (and potentially more given that you have to now make sure your sentinel value is unique).
Hey it's my first attempt at answering a question! I'm a newbie, but I had a similar error recently. I suppose this is a naive version Nigel222's answer which calls for doing this with a migration, but maybe there is something of value here nonetheless for another newbie who needs a simpler solution. I was influenced by an answer to this post by Ayman Al-Absi that suggests that you may need to reference this user by it's auto-generated primary key.
By default, request.user is AnonymousUser when not authenticated. It seems from the error message, AnonymousUser can't be used as value for your foreign key in the User table.
Proposed solution:
from django.contrib.auth.models import User
# Start by creating a user in your User table called something like anon in some kind of initialization method:
tempUser= User.objects.create_user(username="anon", email="none", first_name="none", last_name="none")
tempUser.save()
#when the user is unauthenticated, before calling a method that takes request as a parameter do:
if request.user.is_anonymous:
anonUser = User.objects.get(username='anon')
request.user=User.objects.get(id=anonUser.id)
Another comment for the newbie. I made my own table called User in models.py. This became confusing. I had to import it with an alias:
from .models import User as my_user_table
It would have been better just to call it my_user_table to begin with.
Create a special instance of User for this purpose. It's The best place to do so is in a data migration for the model which will rely on being able to create a ForeignKey to this special User object. When you deploy your app and run makemigrations and migrate, it will create the special user objects before there are any actual users in the DB.
There's a lot of detail on creating data migrations here
Here's an example of making sure that some Group objects will exist as of this migration for any future deployment.
# Generated by Django 2.2.8 on 2020-03-05 09:53
from django.db import migrations
def apply_migration(apps, schema_editor):
Group = apps.get_model("auth", "Group")
Group.objects.bulk_create(
[Group(name="orderadmin"),
Group(name="production"),
Group(name="shipping")]
)
def revert_migration(apps, schema_editor):
Group = apps.get_model("auth", "Group")
Group.objects.filter(name__in=["orderadmin", "production", "shipping"]).delete()
class Migration(migrations.Migration):
dependencies = [
('jobs', '0034_auto_20200303_1810'),
]
operations = [
migrations.RunPython(apply_migration, revert_migration)
]
Hey everyone, I am pretty sure this is a fairly common problem.
So in order to register an account my site you need an email address from certain school domain (like facebook). This wouldn't be that big a problem until you start integrating other apps, like django-notification and django-registration and django-socialregistration into your site where they are sending email via user.email.
I have asked my users and most of them want an 'active_email' option - that means that they can change the email to their designated gmail or whatever.
I have come up with the following solution which isn't the cleanest of all:
First, I inherit from User in django.contrib.auth and call this new class MultipleEmailUser, with email=active_email and official_email=sch_email.
Then I override django.contrib.auth's UserManager to change the API slightly,
And the most painful part is to change all the source code that has User.object.find() to MultipleEmailUser.find().
Can someone suggest me a cleaner way? (My biggest headache arise from other apps only permitting to send email to User.email.)
You don't need - or want - to modify the User class. Just set up a UserProfile class with a OneToOneField back to User, and set the AUTH_PROFILE_MODULE setting. See the documentation.
Rather than a true datastore attribute, you could use a property called 'email' to expose access to whatever data you want.
Basic property syntax (from the python docs):
class C(object):
def __init__(self):
self._x = None
def getx(self):
return self._x
def setx(self, value):
self._x = value
def delx(self):
del self._x
x = property(getx, setx, delx, "I'm the 'x' property.")
In your case, you could create true datastore attributes named, for instance, auth_email and active_email. Then, the User.email property could perform some logic in its getter function, to determine which one to return (i.e. if active_email is set, return it; otherwise, return auth_email)
It's worth noting that the syntax for property has undergone some flux. As of python 2.7, it can be implemented in a more readable way, as a decorator:
class User(BaseModel):
#property # email
def email(self):
if self.active_email:
return self.active_email
return self.auth_email
I asked this in the users group with no response so i thought I would try here.
I am trying to setup a custom manager to connect to another database
on the same server as my default mysql connection. I have tried
following the examples here and here but have had no luck. I get an empty tuple when returning
MyCustomModel.objects.all().
Here is what I have in manager.py
from django.db import models
from django.db.backends.mysql.base import DatabaseWrapper
from django.conf import settings
class CustomManager(models.Manager):
"""
This Manager lets you set the DATABASE_NAME on a per-model basis.
"""
def __init__(self, database_name, *args, **kwargs):
models.Manager.__init__(self, *args, **kwargs)
self.database_name = database_name
def get_query_set(self):
qs = models.Manager.get_query_set(self)
qs.query.connection = self.get_db_wrapper()
return qs
def get_db_wrapper(self):
# Monkeypatch the settings file. This is not thread-safe!
old_db_name = settings.DATABASE_NAME
settings.DATABASE_NAME = self.database_name
wrapper = DatabaseWrapper()
wrapper._cursor(settings)
settings.DATABASE_NAME = old_db_name
return wrapper
and here is what I have in models.py:
from django.db import models
from myproject.myapp.manager import CustomManager
class MyCustomModel(models.Model):
field1 = models.CharField(max_length=765)
attribute = models.CharField(max_length=765)
objects = CustomManager('custom_database_name')
class Meta:
abstract = True
But if I run MyCustomModel.objects.all() I get an empty list.
I am pretty new at this stuff so I am not sure if this works with
1.0.2, I am going to look into the Manager code to see if I can figure
it out but I am just wondering if I am doing something wrong here.
UPDATE:
This now in Django trunk and will be part of the 1.2 release
http://docs.djangoproject.com/en/dev/topics/db/multi-db/
You may want to speak to Alex Gaynor as he is adding MultiDB support and its pegged for possible release in Django 1.2. I'm sure he would appreciate feedback and input from those that are going to be using MultiDB. There is discussions about it in the django-developers mainling list. His MultiDB branch may even be useable, I'm not sure.
Since I guess you probably can't wait and if the MultiDB branch isn't usable, here are your options.
Follow Eric Flows method, bearing in mind that its not supported and new released of Django may break it. Also, some comments suggest its already been broken. This is going to be hacky.
Your other option would be to use a totally different database access method for one of your databases. Perhaps SQLAlchemy for one and then Django ORM. I'm going by the guess that one is likely to be more Django centric and the other is a legacy database.
To summarise. I think hacking MultiDB into Django is probably the wrong way to go unless your prepared to keep up with maintaining your hacks later on. Therefore I think another ORM or database access would give you the cleanest route as then you are not going out with supported features and at the end of the day, its all just Python.
My company has had success using multiple databases by closely following this blog post: http://www.eflorenzano.com/blog/post/easy-multi-database-support-django/
This probably isnt the answer your looking for, but its probably best if you move everything you need into the one database.
I am writing an application where I will be accessing the database from django and from a stand alone application. Both need to do session verification and the session should be the same for both of them. Django has a built in authentication/session verification, which is what I am using, now I need to figure out how to reuse the same session for my stand alone application.
My question is how can I look up a session_key for a particular user?
From what it looks there is nothing that ties together auth_user and django_session
This answer is being posted five years after the original question, but this SO thread is one of the top Google results when searching for a solution to this problem (and it's still something that isn't supported out of the box with Django).
I've got an alternate solution for the use case where you're only concerned with logged in user sessions, which uses an additional UserSession model to map users to their sessions, something like this:
from django.conf import settings
from django.db import models
from django.contrib.sessions.models import Session
class UserSession(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
session = models.ForeignKey(Session)
Then you can simply save a new UserSession instance any time a user logs in:
from django.contrib.auth.signals import user_logged_in
def user_logged_in_handler(sender, request, user, **kwargs):
UserSession.objects.get_or_create(user = user, session_id = request.session.session_key)
user_logged_in.connect(user_logged_in_handler)
And finally when you'd like to list (and potentially clear) the sessions for a particular user:
from .models import UserSession
def delete_user_sessions(user):
user_sessions = UserSession.objects.filter(user = user)
for user_session in user_sessions:
user_session.session.delete()
That's the nuts and bolts of it, if you'd like more detail I have a blog post covering it.
I found this code snippet
from django.contrib.sessions.models import Session
from django.contrib.auth.models import User
session_key = '8cae76c505f15432b48c8292a7dd0e54'
session = Session.objects.get(session_key=session_key)
uid = session.get_decoded().get('_auth_user_id')
user = User.objects.get(pk=uid)
print user.username, user.get_full_name(), user.email
here
http://scottbarnham.com/blog/2008/12/04/get-user-from-session-key-in-django/
Have not verified it yet, but looks pretty straight forward.
This is somewhat tricky to do, because not every session is necessarily associated with an authenticated user; Django's session framework supports anonymous sessions as well, and anyone who visits your site will have a session, regardless of whether they're logged in.
This is made trickier still by the fact that the session object itself is serialized -- since Django has no way of knowing which data exactly you want to store, it simply serializes the dictionary of session data into a string (using Python's standard "pickle" module) and stuffs that into your database.
If you have the session key (which will be sent by the user's browser as the cookie value "sessionid"), the easiest way to get at the data is simply to query the Session table for the session with that key, which returns a Session object. You can then call that object's "get_decoded()" method to get the dictionary of session data. If you're not using Django, you can look at the source code (django/contrib/sessions/models.py) to see how the session data is deserialized.
If you have the user id, however, you'll need to loop through all of the Session objects, deserializing each one and looking for one which has a key named "_auth_user_id", and for which the value of that key is the user id.
Modifying the django_session table to add an explicit user_id can make life a lot easier. Assuming you do that (or something similar), here are four approaches to munging things to your liking:
Fork the django.contrib.session code. I know, I know, that's a horrible thing to suggest. But it's only 500 lines including all backends and minus the tests. It's pretty straightforward to hack. This is the best route only if you are going to do some serious rearranging of things.
If you don't want to fork, you could try connecting to the Session.post_save signal and munge there.
Or you could MonkeyPatch contrib.session.models.Session.save(). Just wrap the existing method (or create a new one), breakout/synthesize whatever values you need, store them in your new fields, and then super(Session, self).save().
Yet another way of doing this is to put in 2 (yes, two) middleware classes -- one before and one after SessionMiddleware in your settings.py file. This is because of the way middleware is processed. The one listed after SessionMiddleware will get, on the inbound request, a request with the session already attached to it. The one listed before can do any processing on the response and/or change/resave the session.
We used a variation on this last technique to create pseudo-sessions for search engine spiders to give them special access to material that is normally member-only. We also detect inbound links where the REFERER field is from the associated search engine and we give the user full access to that one article.
Update:
My answer is now quite ancient, although it still is mostly correct. See #Gavin_Ballard's much more recent answer (9/29/2014) below for yet another approach to this problem.
Peter Rowell, thanks for your response. It was a tremendous help. This is what I did to get it working. Only had to change one file in djang.contrib.sessions.
In django/contrib/sessions/models.py, add the user_id to the table (add to DB table manually or drop table and run manage.py syncdb).
class Session(models.Model):
...
user_id = models.IntegerField(_('user_id'), null=True)
...
def save(self, *args, **kwargs):
user_id = self.get_decoded().get('_auth_user_id')
if ( user_id != None ):
self.user_id = user_id
# Call the "real" save() method.
super(Session, self).save(*args, **kwargs)
Now in your view where you do login (if you use django's base login, you will have to override it)
# On login, destroy all prev sessions
# This disallows multiple logins from different browsers
dbSessions = Session.objects.filter( user_id = request.user.id )
for index, dbSession in enumerate( dbSessions ):
if ( dbSession.session_key != request.session.session_key ):
dbSession.delete()
This worked for me.
I ran across this problem when I wanted to kick out a spammer. It seems setting their account to "inactive" isn't enough, because they can still come in on their previous session. So - how to delete a session for a specific user, or how to deliberately expire a session for a specific user?
The answer is to user the last_login field to track down the time at which the session was disabled, which tells you that the expire_date is two weeks later, which lets you perform a useful filter on the sessions table:
from django.contrib.sessions.models import Session
from django.contrib.auth.models import User
from datetime import datetime
from dateutil.relativedelta import relativedelta
baduser = User.objects.get(username="whoever")
two_weeks = relativedelta(weeks=2)
two_hours = relativedelta(hours=2)
expiry = baduser.last_login + two_weeks
sessions = Session.objects.filter(
expire_date__gt=expiry - two_hours,
expire_date__lt=expiry + two_hours
)
print sessions.count() # hopefully a manageable number
for s in sessions:
if s.get_decoded().get('_auth_user_id') == baduser.id:
print(s)
s.delete()