so i'm making a generic "accounts" page in django. I've used the django-registration plugin, and currently have a (djang-standard) User object, as well as a UserProfile and UserProfileForm object.
This is a question of style, or best-practices, i suppose. Is what i'm planning "right" or is there a "better/recommended/standard way" to do this?
What i'm planning on doing is creating the UserProfile from the request.user ie:
form = UserProfileForm(instance=User)
(and sending that form to the view), and in the UserProfileForm:
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
def __init__(self,*args,**kwargs):
super(UserProfileForm, self).__init__(*args, **kwargs)
if kwargs.has_key('instance'):
self.user = kwargs['instance']
where my UserProfile is pretty much like so:
class UserProfile(models.Model):
user = models.OneToOneField(User)
points = models.IntegerField(default=0) #how is the user going with scores?
and where User is of the django.contrib.auth.models variety.
Ok! The handling of the editing and saving will either be done via the mixin django stuff or, more likely because i haven't read up on mixins my own user-defined view that handles post and gets. But ignoring that - because i'm sure i should be using the mixins - is the above "right?" or are there suggestions?
cheers!
Take a look at user profiles on the django docs, the basics are listed there. You should also take a look at using a form in a view.
Some specific feedback:
You got the UserProfile model right, but you have to create an instance of one every time a new user is added (either through the admin interface or programmatically in one of your views). You do this by registering to the User post_save signal:
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
You should init the ModelForm with an instance of the UserProfile, not User. You can always get the current user profile with request.user.get_profile() (if you define AUTH_PROFILE_MODULE in settings.py). Your view might look something like this:
def editprofile(request):
user_profile = request.user.get_profile()
if request.method == 'POST':
form = UserProfileForm(request.POST, instance=user_profile)
if form.is_valid():
form.save()
return HttpResponseRedirect('/accounts/profile')
else:
form = UserProfileForm(instance=user_profile)
# ...
No need for the init override in your ModelForm. You will be calling it with a UserProfile instance, anyway. If you want to create a new user, just call the User constructor:
user = User()
user.save()
form = UserProfileForm(instance = user.get_profile())
# ...
Related
I should limit choices of manytomanyfield to logged in admin user profile, in django admin.
class News(models.Model):
title=models.CharField(max_length=200)
users=models.ManyToManyField(User, blank=True, limit_choices_to={"profile__school": "request.user.profile.school"})
I have tried to implement it in admin.ModelAdmin, where I can access request.user, but couldn't find a way to do it.
You don't do this in the model layer. Django's user layer is request unaware. Some code paths don't even have a request, for example if these are done through a Python script, or a Django management command.
You can limit the choices in the ModelAdmin by overriding get_form:
class NewsAdmin(ModelAdmin):
def get_form(self, request, obj=None, change=False, **kwargs):
form = super().get_form(request, obj=obj, change=change, **kwargs)
form.base_fields['users'].queryset = User.objects.filter(
profile__school__profile__user=request.user
)
return form
Note: It is normally better to make use of the settings.AUTH_USER_MODELĀ [Django-doc] to refer to the user model, than to use the User modelĀ [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
hello guys in django i tryed to make a simple custom user creation form extends as UserCreationForm .everything works but after save the record in database dont have password and i dont know why is that happening.
help me plz
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
class MyRegistrationForm(UserCreationForm):
email=forms.EmailField(required=True)
first_name = forms.TextInput()
last_name = forms.TextInput()
class Meta:
model=User
fields=('username','email','first_name','last_name','password1','password2')
def save(self,commit=True):
user=super(UserCreationForm,self).save(commit=False)
user.email=self.cleaned_data['email']
user.first_name=self.cleaned_data['first_name']
user.last_name=self.cleaned_data['last_name']
if commit:
user.save()
return user
and in the view the code is :
def register_user_view(request):
if request.method == 'POST':
form=MyRegistrationForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/accounts/success')
args={}
args.update(csrf(request))
args['form']=MyRegistrationForm().as_ul()
print(args)
return render_to_response('register.html',args)
def register_succesfull_view(request):
return render_to_response('registersucc.html')
in database
You need to put the current class, not the parent class, in the call to super:
user=super(MyRegistrationForm, self).save(commit=False)
The way you had it meant that it was skipping the parent and going straight to the grandparent, ie ModelForm, which doesn't have the custom set_password logic.
Note though that since you are using Python 3 you don't need any parameters to super at all:
user=super().save(commit=False)
which would have avoided your problem.
I want to bulk_create models by importing csv data through django admin, with TextArea or FileField. I learned how to override template blocks, how to add new urls to django admin. But I have no idea how to solve my problem. I want to create custom admin page with my form. Pass data, parse it and bulk_create my model objects. Can you guys suggest the way how can I do this?
I found a snippet for this situation
from django.contrib import admin, messages
from django.http import HttpResponseRedirect
from django.shortcuts import render
from my_app.forms import CustomForm
class FakeModel(object):
class _meta:
app_label = 'my_app' # This is the app that the form will exist under
model_name = 'custom-form' # This is what will be used in the link url
verbose_name_plural = 'Custom AdminForm' # This is the name used in the link text
object_name = 'ObjectName'
swapped = False
abstract = False
class MyCustomAdminForm(admin.ModelAdmin):
"""
This is a funky way to register a regular view with the Django Admin.
"""
def has_add_permission(*args, **kwargs):
return False
def has_change_permission(*args, **kwargs):
return True
def has_delete_permission(*args, **kwargs):
return False
def changelist_view(self, request):
context = {'title': 'My Custom AdminForm'}
if request.method == 'POST':
form = CustomForm(request.POST)
if form.is_valid():
# Do your magic with the completed form data.
# Let the user know that form was submitted.
messages.success(request, 'Congrats, form submitted!')
return HttpResponseRedirect('')
else:
messages.error(
request, 'Please correct the error below'
)
else:
form = CustomForm()
context['form'] = form
return render(request, 'admin/change_form.html', context)
admin.site.register([FakeModel], MyCustomAdminForm)
from django import forms
class CustomForm(forms.Form):
# Your run-of-the-mill form here
Using a proxy model would save some typing:
class ImportCSVData(SomeModel):
class Meta:
proxy = True
#admin.register(ImportCSVData)
class MyCustomAdminForm(admin.ModelAdmin):
... as in accepted answer ...
I'm glad to say that since version 1.3.0 django-etc ships with etc.admin.CustomModelPage. So you may want to do something like:
from etc.admin import CustomModelPage
class BulkPage(CustomModelPage):
title = 'Test page 1' # set page title
# Define some fields.
my_field = models.CharField('some title', max_length=10)
def save(self):
# Here implement bulk creation using values
# from self fields attributes, e.g. self.my_field.
super().save()
# Register this page within Django admin.
BulkPage.register()
When I came across this answer, I was hoping to find a way to add a second form to the admin page for an existing model. The answer here gets you sort of close, but there is a much easier way to approach this.
For this example, I will assume the model we're working with is called Candle.
# Make a proxy class for your model, since
# there can only be one admin view per model.
class EasyCandle(models.Candle):
class Meta:
proxy = True
# Make a ModelAdmin for your proxy class.
#admin.register(EasyCandle)
class EasyCandleAdminForm(admin.ModelAdmin):
# In my case, I only want to use it for adding a new model.
# For changing an existing instance of my model or deleting
# an instance of my model, I want to just use the
# views already available for the existing model.
# So has_add_permission returns True while the rest return False.
def has_add_permission(*args, **kwargs):
return True
def has_change_permission(*args, **kwargs):
return False
def has_delete_permission(*args, **kwargs):
return False
# This replaces all the complicated stuff other
# answers do with changelist_view.
def get_form(self, request, obj=None, **kwargs):
return EasyCandleForm
# Finally, make whatever form you want.
# In this case, I exclude some fields and add new fields.
class EasyCandleForm(forms.ModelForm):
class Meta:
model = models.Candle
# Note, do NOT exclude fields when you want to replace their form fields.
# If you do that, they don't get persisted to the DB.
fields = "__all__"
vessel = forms.CharField(
required=True,
help_text="If the vessel doesn't already exist in the DB, it will be added for you",
)
I am overriding the save method on a Django model. There are some cases where I do not save the model. In these cases, I can't seem to be able to figure out how to conditionally override the "The citation 1111 was added successfully." message that is shown after returning back to the admin list interface (as opposed to the entry form interface).
I don't think you can override that just by overriding save Django's admin interface uses model forms and the messages framework.
I think something like this happens, it's more complicated than this but more or less:
models.py
class MyModel(models.Model):
foo = models.CharField(...)
bar = models.CharField(...)
def save(self, *args, **kwargs):
if self.foo == self.bar: # We only save if foo == bar
super(MyModel, self).save(*args, **kwargs)
forms.py (Django admin uses model forms, so this is an example)
class MyModelForm(ModelForm):
class Meta:
model = MyModel
views.py
def save(request):
if request.method == 'POST':
form = MyModelForm(request.POST)
if form.is_valid():
form.save()
messages.success(request, 'MyModel was saved!.')
Now regardless of what form.save() actually did the message is still sent out anyway, we have no way of knowing if you saved or not in your overridden save method and this is probably whats happening in the django admin system.
An alternative would be to create a custom model form for the admin to use for this model and define a custom validation method, so the form doesn't validates unless foo == bar. Or you could override the save method on the form, you'll need to have a look around the django admin code, it probably is possible, just a bit trixy :p
I have a Client model with a OneToOne relationship to User, to extend the User model.
I need to be able to register a user (with a form that includes both the User and Client fields), and for that I used django-registration. Here is the view for registration, based on some snippets I found:
def register_client(request):
if request.method == 'POST':
userform = UserForm(request.POST, prefix='user')
clientform = ClientForm(request.POST, prefix='client')
if userform.is_valid() and clientform.is_valid():
user = userform.save()
client = clientform.save(commit=False)
client.user = user
client.save()
login(request, user)
return HttpResponseRedirect('/webapp/')
else:
return HttpResponse('error!')
else:
userform = UserForm(prefix='user')
clientform = ClientForm(prefix='client')
t = loader.get_template('register.html')
c = RequestContext(request, {
'userform':userform,
'clientform':clientform,
})
return HttpResponse(t.render(c))
And here are my Forms:
from registration.forms import RegistrationForm
class UserForm(RegistrationForm):
def save(self):
new_user = User.objects.create_user(
username=self.cleaned_data['username'],
email = self.cleaned_data['email'],
password=self.cleaned_data['password1'])
new_user.backend='django.contrib.auth.backends.ModelBackend'
new_user.save()
return new_user
class ClientForm(forms.ModelForm):
class Meta:
model = Client
exclude = ['user']
I implemented the save() method for UserForm, since RegistrationForm doesn't implement one like any typical form.
Why does one have to go through all the trouble to implement some backend in order to just save a form?
Is there any problem with this simple way? It does work.
(I also added the new_user.backend='django.contrib.auth.backends.ModelBackend' so I could login the user automatically after registration.)
I'm not sure how to answer your question. If your code works then I don't see a problem. But as zaphod said, django-registration does the saving and activating of the user for you.. If you want to add extra data to your user then use django-profiles like zaphod suggested too.
I myself use django-userena. It is like django-registration and django-profiles in one.
Why do you need to save the User model? django-registration does it for you, unless you need some different functionality.
If you want to store some extra information per user, it might be better to use User profiles.