Multiple user types registration (Django) - python

I'm new to Django and I've read many topics about authentication but I can't still figure out what is the best (or most used) way to set more account types in Django to be able to make different registrations.
Real Example:
I want to create a web page which is dedicated to provide communication and other services between customers and sellers (in this case people who translate across many languages).
The main point is this:
The web should be of course different for those two types of users.
Customer should not have to fill in forms like languages or price per hour during registration.
Another thing is that Seller should have access to pages like "Opened jobs" etc. and Customer should have access to pages like "Estimate price", "Upload text to translate" and many others.
I've already these models:
class Language(models.Model):
shortcut = models.CharField(max_length=6)
name = models.CharField(max_length=50)
price_per_sign = models.FloatField()
class BaseUser(AbstractUser):
# username = models.CharField(max_length=50)
# email = models.EmailField(unique=True)
# first_name = models.CharField(max_length=50)
# surname = models.CharField(max_length=50)
telephone = models.CharField(max_length=50)
class TranslatorUser(BaseUser):
languages = models.ManyToManyField(Language)
class Meta:
verbose_name = 'Translator'
verbose_name_plural = 'Translators'
class CustomerUser(BaseUser):
spent = models.FloatField()
class Meta:
verbose_name = 'Customer'
verbose_name_plural = 'Customers'
How to create a registration forms for both Customer and Translator?
This is my forms.py:
class TranslatorUserRegistrationForm(UserCreationForm):
username = forms.CharField(required=True)
email = forms.EmailField(required=True)
class Meta:
model = User
fields = ['email','username']
def save(self, commit=True):
user = super(TranslatorUserRegistrationForm,self).save(commit=True)
translator = TranslatorUser(user=user,email=self.cleaned_data['email'],username=self.cleaned_data['username'])
translator.save()
return user, translator
And views.py:
def register_translator(request):
form = TranslatorUserRegistrationForm()
if request.method==['POST']:
form = TranslatorUserRegistrationForm(request.POST)
else: return render(request,'registration/register_form.html',{'registration_form':form})
When I try to make migrations, it returns this error:
ERRORS:
auth.User.groups: (fields.E304) Reverse accessor for 'User.groups' clashes with
reverse accessor for 'BaseUser.groups'.
HINT: Add or change a related_name argument to the definition for 'User.
groups' or 'BaseUser.groups'.
auth.User.user_permissions: (fields.E304) Reverse accessor for 'User.user_permis
sions' clashes with reverse accessor for 'BaseUser.user_permissions'.
HINT: Add or change a related_name argument to the definition for 'User.
user_permissions' or 'BaseUser.user_permissions'.
auth_test.BaseUser.groups: (fields.E304) Reverse accessor for 'BaseUser.groups'
clashes with reverse accessor for 'User.groups'.
HINT: Add or change a related_name argument to the definition for 'BaseU
ser.groups' or 'User.groups'.
auth_test.BaseUser.user_permissions: (fields.E304) Reverse accessor for 'BaseUse
r.user_permissions' clashes with reverse accessor for 'User.user_permissions'.
HINT: Add or change a related_name argument to the definition for 'BaseU
ser.user_permissions' or 'User.user_permissions'.

Solution that I'm using is based on Profile models. Those are models that will extend normal user model (by one to one relation) and won't contain authentication data. That type of models are described in django docs, but they are made simply by OneToOneField.
I'm using solution based on model inheritance. In django models that aren't abstract also can be inherited, django will under the hood create OneToOneField in child model pointing to parent model. That field will also be used as an primary key, so ID of child model will always be same as it's parent. Also, you will be able to access parent fields from child models (like they we're here!).
So in other words, you should create 2 models (one for each type of user) that will inherit from standard User model (or customized one, but it can't be abstract) and for each registered user you will create object from one of that models.
For easy use, you can create middleware that will automatically detect type of user and swap user model in request object to specialized one.

Related

Django, Many-to-one relationship with Abstract entities

Say object contains multiple types of objects (Models) in an array, like a Page, that could contain a Post, Blog, Picture, in any quantity or order, where the user scrolls down and sees the following element and that depending on the page, is next element one of these types of elements. Thus a Page could contain:
A Post
A Post
A Picture
Or something pretty different such as:
A Picture
A Blog
A Picture
An element could be an abstract entity that has an attribute as a ForeignKey relating itself with Page, and its inheritance would gain access to that relationship:
class Page(models.Model):
...
class PageElement(models.Model):
page = models.ForeignKey(Page, verbose_name="Page", on_delete=CASCADE)
class Meta:
abstract = True
class Post(PageElement):
...
class Blog(PageElement):
...
class Picture(PageElement):
...
With this approach, you can serialize Page with a REST serializer, but you wouldn't have access to an pageElement_set, instead you would need to call post_set, blog_set, picture_set and loose the order of the objects.
By just adding a related_name key in the ForeignKey, we get an error:
pages.Picture.page: (fields.E305) Reverse query name for 'Picture.page' clashes with reverse query name for 'Post.page'.
HINT: Add or change a related_name argument to the definition for 'Picture.page' or 'Post.page'.
The question is: How do you implement such relationships in Django?
So that you can have an order set of PageElement instances, and be able to have the desired order.
Since you want to query multiple Models in order and you can see that your models are in Model is a PageElement relationship, then instead of inheriting from an abstract Model you need to have a OneToOneField with a common model:
class PageElement(models.Model):
POST = 'PO'
BLOG = 'BL'
PICTURE = 'PI'
ELEMENT_TYPE_CHOICES = [
(POST, 'Post'),
(BLOG, 'Blog'),
(PICTURE, 'Picture'),
]
element_type = models.CharField(
max_length=2,
choices=ELEMENT_TYPE_CHOICES,
default=POST,
)
# common fields
class Post():
page_element = models.OneToOneField(
PageElement,
on_delete=models.CASCADE,
related_name = "post"
)
class Blog():
page_element = models.OneToOneField(
PageElement,
on_delete=models.CASCADE,
related_name = "blog"
)
class Picture():
page_element = models.OneToOneField(
PageElement,
on_delete=models.CASCADE,
related_name = "picture"
)
You can inherit from a common abstract class that has the OneToOneField for ease if needed.

Problems with the Follow function

I want created a kind social network for my school in django.
My Problem is that I create a follow function and django give me a Error Response.
How can I handle that?
django code:
class User(User):
joined = models.DateTimeField(auto_now_add=True)
is_activated = models.BooleanField(default=False)
def __unicode__(self):
return self.username
class Following_User(models.Model):
user_id = models.ForeignKey(User)
user_follow = models.ForeignKey(User)
and heres the Error Response:
ERRORS:
cherry.Following_User.user_follow: (fields.E304) Reverse accessor for 'Following_User.user_follow' clashes with reverse accessor for 'Following_User.user_id'.
HINT: Add or change a related_name argument to the definition for 'Following_User.user_follow' or 'Following_User.user_id'.
cherry.Following_User.user_id: (fields.E304) Reverse accessor for 'Following_User.user_id' clashes with reverse accessor for 'Following_User.user_follow'.
HINT: Add or change a related_name argument to the definition for 'Following_User.user_id' or 'Following_User.user_follow'.
System check identified 2 issues (0 silenced).
If you are having a model that contains two fields that as foreign key of another model, you need to specify the related_name for each of them.
class Following_User(models.Model):
user_id = models.ForeignKey(User, related_name="following_user_id")
user_follow = models.ForeignKey(User, related_name="following_user_follow")
https://docs.djangoproject.com/en/1.8/ref/models/fields/#django.db.models.ForeignKey.related_name
The reason to have related_name is so that django would give you reverse relation if you want to query from the foreign key. For single field that has the relationship with current model, django gives you a default one blahfield_set, but in this case the related_name would be used to remove ambiguity.

Django models: user ID

I'm currently building a helpdesk ticketing system as a school project. I am using the built in django auth system, and I'd like to refer to user IDs from the auth sytem. For example, a ticket will be assigned to a certain helpdesk employee. A part of my model:
class Ticket(models.Model):
category = models.ManyToManyField(Category)
title = models.CharField(max_length=30)
submitted = models.DateTimeField(editable=False)
submitter = #reference to user
assignedTo = #reference to helpdesk employee
Users are in the group user, helpdesk employees are in the group helpdeskemployee of the django auth system.
I already found this and this
So I tried this:
class Ticket(models.Model):
category = models.ManyToManyField(Category)
title = models.CharField(max_length=30)
submitted = models.DateTimeField(editable=False)
submitter = models.OneToOneField(User)
assignedTo = user = models.OneToOneField(User)
But that gives the folowing error while running python manage.py syncdb:
CommandError: One or more models did not validate:
deskman.ticket: Accessor for field 'submitter' clashes with related field 'User.ticket'. Add a related_name argument to the definition for 'submitter'.
deskman.ticket: Reverse query name for field 'submitter' clashes with related field 'User.ticket'. Add a related_name argument to the definition for 'submitter'.
deskman.ticket: Accessor for field 'assignedTo' clashes with related field 'User.ticket'. Add a related_name argument to the definition for 'assignedTo'.
deskman.ticket: Reverse query name for field 'assignedTo' clashes with related field 'User.ticket'. Add a related_name argument to the definition for 'assignedTo'.
deskman.ticket: Accessor for field 'assignedTo' clashes with related field 'User.ticket'. Add a related_name argument to the definition for 'assignedTo'.
deskman.ticket: Reverse query name for field 'assignedTo' clashes with related field 'User.ticket'. Add a related_name argument to the definition for 'assignedTo'.
Firstly, you probably don't want to use a OneToOneField. That would imply that a user can only ever have one single ticket. A ForeignKey relationship would be better.
class Ticket(models.Model):
category = models.ManyToManyField(Category)
title = models.CharField(max_length=30)
submitted = models.DateTimeField(editable=False)
submitter = models.ForeignKey(User)
assignedTo = models.ForeignKey(User)
The reason you are getting an error is that you have two relationships to the same model from your Ticket. This means if you have a User object and you are trying to reverse it, it's not clear via which relationship you want to use:
user.ticket_set.all()
# Do you want `submitter` tickets, or `assignedTo` tickets? It's not clear
To fix it, add a related_name attribute to each field
class Ticket(models.Model):
category = models.ManyToManyField(Category)
title = models.CharField(max_length=30)
submitted = models.DateTimeField(editable=False)
submitter = models.ForeignKey(User, related_name="tickets_submitter")
assignedTo = models.ForeignKey(User, related_name="tickets_assignedto")
Now you can get both reverse relationship separately:
user.tickets_submitter.all()
user.tickets_assignedto.all()

Django GenericForeignKey: accessor clash when 2 models have same related_name

When I syncdb, I get many errors like this:
transcription.transcription1: Accessor for field 'participant_content_type' clashes with related field 'ContentType.auxi
liary_model_as_participant'. Add a related_name argument to the definition for 'participant_content_type'.
transcription.transcription1: Reverse query name for field 'participant_content_type' clashes with related field 'Conten
tType.auxiliary_model_as_participant'. Add a related_name argument to the definition for 'participant_content_type'.
My models already have related names:
# my base class which I intend to inherit from in many places.
# Many types of AuxiliaryModel will point at participant/match objects.:
class AuxiliaryModel(models.Model):
participant_content_type = models.ForeignKey(ContentType,
editable=False,
related_name = 'auxiliary_model_as_participant')
participant_object_id = models.PositiveIntegerField(editable=False)
participant = generic.GenericForeignKey('participant_content_type',
'participant_object_id',
)
match_content_type = models.ForeignKey(ContentType,
editable=False,
related_name = 'auxiliary_model_as_match')
match_object_id = models.PositiveIntegerField(editable=False)
match = generic.GenericForeignKey('match_content_type',
'match_object_id',
)
class Meta:
abstract = True
class Transcription(AuxiliaryModel):
transcription = models.TextField(max_length=TRANSCRIPTION_MAX_LENGTH,
null=True)
class Meta:
abstract = True
class Transcription1(Transcription):
pass
class Transcription2(Transcription):
pass
class Transcription3(Transcription):
pass
The problem goes away when I comment out Transcription2 and Transcription3, so it seems like the related_names clash. Do I have to make them unique? If so, is there a way to do this without having to write boilerplate code in each child class?
From the Django docs https://docs.djangoproject.com/en/dev/topics/db/models/#be-careful-with-related-name :
If you are using the related_name attribute on a ForeignKey or ManyToManyField, you must always specify a unique reverse name for the field. This would normally cause a problem in abstract base classes, since the fields on this class are included into each of the child classes, with exactly the same values for the attributes (including related_name) each time.
To work around this problem, when you are using related_name in an abstract base class (only), part of the name should contain '%(app_label)s' and '%(class)s'.
In this case, I think this will work:
participant_content_type = models.ForeignKey(ContentType,
editable=False,
related_name = '%(app_label)s_%(class)s_as_participant')
match_content_type = models.ForeignKey(ContentType,
editable=False,
related_name = '%(app_label)s_%(class)s_model_as_match')
So, using %(app_label)_transcription2_as_participant you can access the reverse of Transcription2.participant_content_type

Django "Add a related_name argument to the definition" Error

models.py
class BaseComment(models.Model):
comment_author = models.ForeignKey(MyUser, related_name='written_comments')
comment_content = models.CharField(max_length=500)
comment_date = models.DateTimeField(auto_now_add=True)
rating = models.IntegerField(default=0)
users_voted = models.ManyToManyField(MyUser, related_name='voted_comments')
class Meta:
abstract = True
ordering = ['-comment_date']
def __unicode__(self):
return self.comment_author.email
class QuestionComment(BaseComment):
question = models.ForeignKey(Question, related_name='comments')
class AnswerComment(BaseComment):
answer = models.ForeignKey(Answer, related_name='comments')
This is my models.py. When I run syncdb, I get this error:
CommandError: One or more models did not validate:
app_quora.questioncomment: Accessor for field 'comment_author' clashes with rela
ted field 'MyUser.written_comments'. Add a related_name argument to the definiti
on for 'comment_author'.
app_quora.questioncomment: Reverse query name for field 'comment_author' clashes
with related field 'MyUser.written_comments'. Add a related_name argument to th
e definition for 'comment_author'.
app_quora.questioncomment: Accessor for m2m field 'users_voted' clashes with rel
ated m2m field 'MyUser.voted_comments'. Add a related_name argument to the defin
ition for 'users_voted'.
app_quora.questioncomment: Reverse query name for m2m field 'users_voted' clashe
s with related m2m field 'MyUser.voted_comments'. Add a related_name argument to
the definition for 'users_voted'.
app_quora.answercomment: Accessor for field 'comment_author' clashes with relate
d field 'MyUser.written_comments'. Add a related_name argument to the definition
for 'comment_author'.
app_quora.answercomment: Reverse query name for field 'comment_author' clashes w
ith related field 'MyUser.written_comments'. Add a related_name argument to the
definition for 'comment_author'.
app_quora.answercomment: Accessor for m2m field 'users_voted' clashes with relat
ed m2m field 'MyUser.voted_comments'. Add a related_name argument to the definit
ion for 'users_voted'.
app_quora.answercomment: Reverse query name for m2m field 'users_voted' clashes
with related m2m field 'MyUser.voted_comments'. Add a related_name argument to t
he definition for 'users_voted'.
Well, the error tells me to add the "related_name" for both "comment_author" and "users_voted", which I ALREADY DID!!. I looked up similar questions on stackoverflow, but usually the issue was that people didn't have this "related_name" for clashing fields.
I added this fields, but am still getting this error... Can someone explain why?
Thanks :(((
The problem you are seeing is because of the ForeignKey and ManyToManyField fields in the BaseComment model. Because it is an abstract model, django does not create a table for it.
However, the derived models QuestionComment and AnswerComment which inherit the fields try to add the same fields defined by related_name (written_comments , voted_comments ) in the MyUser model. Because you can have only one field with the same name it gives you those errors.
A solution would be to not have BaseComment as an abstract model and let it have its own table/model.
Or, you could add these fields in derived models.
You can also try not specifying related_name and let django decide the names, but I'm not sure if this will work or not.

Categories

Resources