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
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 a form that takes in user data like bio, profile picture, gender, etc. and upon submission either creates this new row about the user or updates the existing row. This will only work if the user uploads an image. If no image is uploaded for the profile picture, then the form doesn't submit. How can I make it so that if the user didn't upload a profile picture, then it'll keep the user's previous profile picture and still submit?
Here's my code:
class ProfileSettings(UpdateView):
model = Profile
template_name = 'blog/settings.html'
form_class = ProfileForm
success_url = reverse_lazy('blog:settings')
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST, request.FILES)
if form.is_valid():
bio = form.cleaned_data['bio']
gender = form.cleaned_data['gender']
avatar = form.cleaned_data['avatar']
Profile.objects.update_or_create(user=self.request.user, defaults={'avatar':avatar, 'bio':bio, 'gender':gender})
return HttpResponseRedirect(self.success_url)
I'll give you the quick and dirty - 3 places to solve this:
Javascript - make the form aware of what fields are required and pre-fill if the username already exists (out of scope from your question but just throwing it out there)
In the API endpoint (this seems to be the approach you are going for)
In your model (implement a custom save function that looks to see if new, and compare initial value to subsequent value)
I'll dump options 1 and 3 because they aren't pertinent to your question as asked. I'm assuming user is unique per profile. And I'm assuming that currently the field avatar is required. If you set that to not required then the form post should allow a null value for avatar - How to make FileField in django optional?. You may be thinking, but I don't want that field to be possibly blank - you could always enforce that the first time the post is made that the field is set via the API endpoint itself. If you made that field optional then the form would post but you may want to be more explicit with .update_or_create by actually checking to see if the object already exists and if so require the field or if not confirm that field is set.
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.
Need help accessing Hidden inputs in a Form clean_data or clean function. Unable to find solution here and Django docs after about 1 hr of searching.
class AccountForm(forms.ModelForm):
class Meta:
model = Account
#action = forms.CharField(widget=forms.HiddenInput()) <--- also tried this
exclude = [
"a_account_number"
]
# Validate that there isn't already an account that exists with a similar company name during account creation
def clean_a_company_name(self):
logging.debug("Value of action %s") % self.data.__getitem__('action')
if Account.objects.filter( a_company_name = self.cleaned_data['a_company_name']).exists() and % self.data.__getitem__('action')== 'create':
logging.debug("In account views - form validation - clean_a_company - company already exists raising exception *****************")
raise forms.ValidationError(u"An organization with this name and owner already exists. Change the Organization name or edit Organization Account information instead of creating a new Organization Account")
return self.cleaned_data["a_company_name"]
Above gives a unicode error. I also tried:
%self.fields['action']
So you are trying to access action field in method for cleaning a_company_name?
You don't have access to other field in field's clean method. You should use form's clean method.
From django docs:
The Form subclass’s clean() method.
This method can perform any validation
that requires access to multiple
fields from the form at once. This is
where you might put in things to check
that if field A is supplied, field B
must contain a valid email address and
the like.
https://docs.djangoproject.com/en/dev/ref/forms/validation/#cleaning-and-validating-fields-that-depend-on-each-other
I have the following models (simplified example):
class Book(models.Model):
users = models.ManyToManyField(User, through=Permission)
class Permission(models.Model):
user = models.ForeignKey(User)
role = models.ForeignKey(Group)
active = models.BooleanField()
book = models.ForeignKey(Book)
What I need is that for a Book instance there cannot be more than one User of with the same Role and Active.
So this is allowed:
Alice, Admin, False (not active), BookA
Dick, Admin, True (active), BookA
Chris, Editor, False (not active), BookA
Matt, Editor, False (not active), BookA
But this is not allowed:
Alice, Admin, True (active), BookA
Dick, Admin, True (active), BookA
Now this cannot be done with unique_together, because it only counts when active is True. I've tried to write a custom clean method (like how I have done here). But it seems that when you save a Book and it runs the validation on each Permission, the already validated Permission instances aren't saved until they've all been validated. This makes sense, because you don't want them to be saved in case something doesn't validate.
Could anyone tell me if there is a way to perform the validation described above?
P.S. I could imagine using the savepoint feature (http://docs.djangoproject.com/en/1.2/topics/db/transactions/), but I only really want to consider that as a last resort.
Maybe you can do something like: unique_together = [[book, role, active=1],] ?
Edit Sep. 23, 2010 14:00 Response to Manoj Govindan:
My admin.py (simplified version for clarity):
class BookAdmin(admin.ModelAdmin):
inlines = (PermissionInline,)
class PermissionInline(admin.TabularInline):
model = Permission
In the shell your validation would work. Because you first have to create the book instance and then you create all the Permission instances one by one: http://docs.djangoproject.com/en/1.2/topics/db/models/#extra-fields-on-many-to-many-relationships. So in the shell if you add 2 Permission instances the 1st Permission instance has been saved by the time 2nd is being validated, and so the validation works.
However when you use the Admin interface and you add all the book.users instances at the same time via the book users inline, I believe it does all the validation on all the book.users instances first, before it saves them.
When I tried it, the validation didn't work, it just succeeded without an error when there should have been a ValidationError.
You can use signals to prevent the saving of data that is invalid: I'm still working on a nice solution on how to get the validation to bubble up in a nice way in the admin.
#receiver(models.signals.m2m_changed, sender=Book.users.through)
def prevent_duplicate_active_user(sender, instance, action, reverse, model, pk_set, **kwargs):
if action != "pre_add":
return
if reverse:
# Editing the Permission, not the Book.
pass
else:
# At this point, look for already saved Users with the book/active.
if instance.permissions.filter(active=True).exists():
raise forms.ValidationError(...)
Note that this is not a complete solution, but is a pointer how I am doing something similar.
One way to do this is to use the newfangled model validation. Specifically, you can add a custom validate_unique method to Permission models to achieve this effect. For e.g.
from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
class Permission(models.Model):
...
def validate_unique(self, exclude = None):
options = dict(book = self.book, role = self.role, active = True)
if Permission.objects.filter(**options).count() != 0:
template = """There cannot be more than one User of with the
same Role and Active (book: {0})"""
message = template.format(self.book)
raise ValidationError({NON_FIELD_ERRORS: [message]})
I did some rudimentary testing using one of my projects' Admin app and it seemed to work.
Now this cannot be done with unique_together, because it only counts when active is True.
Simplest way, imo, is to change type of active from BooleanField to CharField. Store 'Y' and 'N' in active.
That way you can use built-in unique_together = [[book, role, active],]