I have a django model which stores user and product data from a form input:
def ProductSelection(request, template_name='product_selection.html'):
...
if user.is_authenticated():
user = request.user
else:
# deal with anonymous user info
project = Project.objects.create(
user=user,
product=form.cleaned_data["product"],
quantity=form.cleaned_data["product_quantity"],
)
Of course this is fine for authenticated users, but I also want to be able to store anonymous user projects, and if possible, associate them with the user when they eventually register and authenticate.
My idea is to create anonymous user with name = some_variable (timestamp concatenated with a random hash?), then save that username in session data. If I ensure that that session variable, if exists, is used to record all projects activity of that user, I should be able to update the projects with the user's real credentials when they register.
Is this overly complicated and brittle? Do I risk saving thousands of rows of data unnecessarily? What would be the optimal approach for this common issue?
Any guidance on this would be much appreciated.
You can use Django's session framework to store anonymous user data.
You can then either add a field to your Project model to hold the session_key value for anonymous users,
project = Project.objects.create(
user=request.user, # can be anonymous user
session=request.session.session_key,
product=form.cleaned_data["product"],
quantity=form.cleaned_data["product_quantity"])
or simply store all the data a Project instance would have in the session
if user.is_authenticated():
project = Project.objects.create(
user=request.user,
product=form.cleaned_data["product"],
quantity=form.cleaned_data["product_quantity"])
else:
# deal with anonymous user info
request.session['project'] = {
"product": form.cleaned_data["product"],
"quantity": form.cleaned_Data["product_quantity"]}
You can retrieve the data from the session later, when creating a proper user.
Just to clarify, the below code is how implemented the solution in my case:
project = Project.objects.create(
session=request.session.session_key,
# save all other fields
...
)
if request.user.is_authenticated():
project.user = request.user
else:
# make a copy of the session key
# this is done because the session_key changes
# on login/ register
request.session['key_copy'] = request.session.session_key
project.save()
And in my models.py:
class Project(models.Model):
user = models.ForeignKey(User, null=True, blank=True)
...
So a user field can be null, and in this case we use the session_key to keep a track of things.
Related
I'm building an app to manage flats:
There are different administrations. Each administration has users. Every administrations owns properties and every property has 1 to n flats.
Sofar so good. That's more or less setup.
No comes the tricky part. The users of administration A should only be allowed to see properties and flats that their administration owns.
How would I best do this?
Assuming that there is a ForeingKey relationship between the Administrator model and the User model, you can right a filter on what the user can see. For example:
class UserCreatesAndViewsSomething(LoginRequiredMixin, CreateView):
model = UserLog (or something like that)
template_name = 'some template you have'
fields = ('field you want to display',)
# This function will submit the form to the database
def form_valid(self, form):
# this will help you determine what the current administrator is
administrator = AdministratorModel.objects.get(user=self.request.user, administrator=self.kwargs['administrator'])
form.instance.user = self.request.user
# This will autopopulate administrator input to the current user administrator
form.instance.administrator = administrator
return super(UserCreatesAndViewsSomething, self).form_valid(form)
# get_context_data will help you determine what the user can see.
def get_context_data(self, **kwargs):
administrator = AdministratorModel.objects.get(user=self.request.user, administrator=self.kwargs['administrator'])
context = super(UserCreatesAndViewsSomething, self).get_context_data(**kwargs)
context['something'] = to_journal_entry.objects.filter(user=self.request.user, administrator=administrator)
return context
I know that's a lot, but if you are at all familiar with Django you can certainly do it. You will have to go through some trial and error, but this is the approach I used for my project.
Finale note, this assumes all your users and user inputs are in the same database and the code helps you get only the relevant information. If you are dealing with high importance clients or or some sensitive information you should probably look into multi tenancy, which will set up a different schema or a different database for each of your clients. This will lead a different code structure.
Hope this helps.
I have 3 types of users in my models.py
class Customer(models.model)
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name=‘Customer’)
class ClientTypeA(models.model)
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name=‘ClientA’)
class ClientTypeB(models.model)
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name=‘ClientB’)
I was using permissions in my base template to render the correlating sidebar, but now I am also incorporating a specific dashboard for each user along with the sidebar, so I find it would be simpler to create home views for each of the user types.
Once I log a user in it redirects them to my home view - so I came up with this in my views.py
def home(request):
if request.user.is_customer:
return redirect(customer_home)
elif request.user.is_clientA:
return redirect(clientA_home)
elif request.user.is_clientB:
return redirect(clientB_home)
else:
return render(request, 'home.html')
The redirects called will simply take them to there corresponding home pages.
I know my is_customer, is_clientA, is_clientB do not automatically come with django, how and where do I add these custom permissions? to my models.py? What would I set them equal to in order to simply check is the type of user active? Do I even need custom permissions or is there a simpler way to call the type of user?
Am I using to many if statements? (I'm trying to keep it simple and fast)
Since all the models have one-to-one fields to the User model, you can use has_attr to check whether the user has a row for that model. You don't have to create permissions.
if hasattr(request.user, 'customer'):
return redirect('customer_home')
If you only have three customer types, then the if/elif/else is probably ok. As the number increases, you can change it to something like the following.
customer_types = [
('customer', 'customer_home'),
('clienttypea', 'ClientA_home'),
...
]
for field_name, redirect_url in customer_types:
if hasattr(request.user, field_name):
return redirect(redirect_url)
# fallback
return render(request, 'home.html')
I have a model, Package:
class Package(models.Model):
VIP = models.BooleanField()
name = models.CharField(max_length=200)
contents = models.CharField(max_length=200)
owner = # a string, user name goes here
For any particular instance of Package (that is, each row of the database), I want that only the user whose username matches owner can modify this instance, via the admin interface. How can I do this?
Override the has_change_permission for your model admin.
class PackageAdmin(admin.ModelAdmin):
def has_change_permission(self, request, obj):
if request.user.is_super_user():
# allow superusers to edit all packages
return True
if obj is None:
# The docs say that the method should handle obj=None
# Don't allow user to edit packages in general
return False
# Let the user edit the package if they are the owner.
return obj.owner == request.user
The code above assumes that owner is a foreign key, which I recommend. If you really want to store the username as a string, then you would change the last line to:
return obj.owner == request.user.username
You could add an extra check to make sure that the user has the 'change' permission for the Package model as well (see the docs for more info).
Note there is a has_delete_permission model admin method, which you might want to override as well.
In my current project, I'd like to CRUD users OUTSIDE of django's Admin interface.
Let's explain my question as following:
1- I'm using the UserProfile for storing additional attributes for users (their schools, birthday, etc.)
2- The problem is that by deleting a user, I can delete the profile rather than actual User.
Please take at the code for Listing and Deleting Users:
def user_list(request):
''' Shows all of Students '''
return object_list(request,
queryset = UserProfile.objects.all() ,
template_name = 'user_list.html' ,
template_object_name = 'student'
)
def user_delete(request , id):
''' Deletes a student based on his/her ID '''
return delete_object(request,
model = UserProfile ,
object_id = id ,
template_name = 'delete_student.html' ,
post_delete_redirect = reverse("user_list")
)
It looks normal that I'm deleting UserProfile rather than User. But I intended it to be a proxy to actual User. Do I miss something here ?
3- Generally speaking, should I reference each models to User or UserProfile ? For example assume that I have a model for Course. Which of these is the correct way?
class Course(models.Model):
#stuff
student = models.ForeignKey(Urer)
# OR ??
student = models.ForeignKey(UserProfile)
It looks normal that I'm deleting UserProfile rather than User. But I intended it to a proxy to actual User
Why not delete the User directly? By default, Django will CASCADE DELETE to get rid of the UserProfile as well.
Generally speaking, should I reference each models to User or UserProfile
I think this is more a question of personal preference, but I usually tie to User directly as it saves a step when getting to the object you want (you don't need to do user.get_profile().student and can call user.student instead). It also makes more sense to me anyway: the student is a property of the user, not the user's profile.
Some background,
I've been designing a user profile for our Django database system and am currently trying to create a views page to allow the user to edit their account settings (outside of the admin page). I have searched numerous sources, most of which recommend using .get_profile(), but this has not worked in my program no matter what approach I try (to my knowledge and research done). So far I have used the django main pages for .get_profile() to see how to properly use .get_profile() and found the AUTH_PROFILE_MODULE setting ( I will specify my settings in a bit).
Using this site:
http://www.turnkeylinux.org/blog/django-profile I found out some other methods of using .get_profile() but still nothing has worked. (I won't add the rest of the links i've tried as I could go on for a while)
Back to the question, can anyone recommend a method that would work so that I can obtain the users information to set up some default values to maintain their current settings if they choose not to edit them and only update the ones with new submitted values.
So far I have: (hopefully all the relevant parts)
file directory:
~/Documents/project1
settings.py
AUTH_PROFILE_MODULE = "accounts.Account"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`
File location:
project1/accounts
models.py
# additional model to incorporate our custom fields to the auth user model
class Account(models.Model):
userLink = models.OneToOneField(User) #link (pointer) to the users other information in User model
birthdate = models.DateField(blank = True) # True makes this field optional
gender = models.CharField(max_length = 1, choices = GENDER_CHOICE, null = True)
def __unicode__(self): # define a unicode for the user to access
return u'%s %s' % (self.userLink.first_name, self.userLink.last_name) # return first and last name
# custom Form to change user account information (can't be a modelForm)
class EditAccountForm(forms.Form):
gender = forms.CharField(max_length = 1)#, choices = GENDER_CHOICE, null = True)
birthdate = forms.DateField(widget = SelectDateWidget()) # True makes this field optional
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
views.py
# Account settings view
#login_required
def AccountSettings(request):
sluggedSettingError = request.GET.get('error', '') # error message with slugged character
settingError = sluggedSettingError.replace('-', ' ')
settingForm = AdditionalForm(request.POST or None) # variable is called in edit_user.html
# might want to have some way to create a new profile if the account doesn't exist
userSettings = Account.objects.filter(userLink=request.user)
# userSettings = request.user.get_profile() # returns the current settings of the users profile
#### Currently trying to get it to let me use get_profile() ########
print "userLink = %s" % userSettings#.user
# print "Gender = %s" % userSettings.gender # testing print
if request.method == 'POST':
# if settingForm.is_valid():
# settingForm.save();
return HttpResponseRedirect('/')
return render_to_response("user_settings.html", {'settingForm': settingForm, 'settingError': settingError}, context_instance = RequestContext(request))
# gender = request.POST['gender'] # pass in the gender variable from post
# birthdate = request.POST['birthdate'] # pass in the birthdate from post
As the code is currently I gave up on the .get_profile() approach and tried doing a query through the available accounts until a matching userLink was found and then saving that to a variable "userSettings". The approach I was taking was to use this variable as userSettings.gender, userSettings.birthdate etc. to access each of the users settings and have them changed if updated and set to the previous value if not (with a series of if statements or some built in Django function).
I'm not sure if this is the most efficient method, or if I should revert back to the get_profile() method. When I was using get_profile() (as it is typed currently in the commented out line) I got the following error
Cannot resolve keyword 'user' into field. Choices are: birthdate, gender, id, userLink
I also tried this approach with a new database using userLink defined as "user" instead to match what the django.docs website specified but still got an error about the Account not existing. I tried creating a try: and except: approach from
https://bitbucket.org/filmaster/filmaster-test/src/1618b18003ed/film20/userprofile/views.py
to handle this error but this also did not yield any positive results.
I am out of ideas on how to get .get_profile() to work, and can't think of anything to allow me to access the users stored information so that it can be edited in a form by them. I'm not sure I understand how to utilize it properly at this point and feel like I'm just running around in circles at this point. If anyone can help me with this I'd appreciate it. I tried to give as much relevant information as I could, but if you need something else to help me out let me know and I will try to include it as soon as I can.
Thanks in advance!
To get the profile attached to a user object, you just need to call get_profile() on the user object:
user.get_profile()
For example,
request.user.get_profile()
The docs on it are in Django Docs. Its worth noting that
The method get_profile() does not create a profile if one does not exist.
So you have to have a trigger to create profiles on creating users ...
user.get_profile() is now depreciated, instead, assuming you have a OneToOne with the user model, you can just use user.profile