Creating several profile classes in django - python

I'm getting started with django and I'd like to extend the basic django.contrib.auth.models.User class to create my own site profile(s). Here is described how to do it, got that.
As far as I've understood it, you can only specify a single class as AUTH_PROFILE_MODULE in your settings.py.
Now, if I create an extension class of my profile class like this
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
somefield = models.CharField()
class UserProfileExtended(UserProfile):
extrafield = models.CharField()
then I cannot make both of them profile classes, right?
(I know, in this case you'd just add the extrafield to the superclass and drop the UserProfileExtended entirely. Just imagine you have so many fields in UserProfileExtended that you really want to split them up)
Thanks for your help!

There can be only one profile class. I guess I don't understand the scenario where you would want to split them up. In any case,
AUTH_PROFILE_MODULE = "UserProfileExtended"
should handle the inheritance correctly for the simple example you give.

Related

Django Models Generic Relations

I'm confused with generic relationships in Django.
I have a comment model, and I want both Workflow and WorkflowItem models to be able to have multiple comments.
If I do:
class Workflow(models.Model):
comments = models.ManyToManyField(Comment)
class WorkflowItem(models.Model):
comments = models.ManyToManyField(Comment)
then what do I put in the comment class to link the comment to one of these based on which it is or do I need generic relationships?
Also say I want to put members who are part of the Workflow model, do i do
class Workflow(models.Model):
comments = models.ManyToManyField(Comment)
members = models.ManyToManyField(Person)
or something else?
As you mentioned that you need to link comment back to either Workflow/WorkflowItem, I believe you can structure your models as below
class Workflow(models.Model):
members M2M field
class WorkflowItem(models.Model):
fields
class Comment(models.Model):
name_of_your_generic_fk(Can be either Workflow/WorkflowItem or any content type for that matter)
fields
Using models structure like this you can trace from comment if it was made on Workflow/WorkflowItem.
You can obviously devise a better solution if you put more thought into it!! :)

How to inject a many-to-many relationship into third-party models?

I want to insert a many-to-many relationship into some models, without having to completely rewrite the models.
For example, consider the django User model and a model Foo from a third-party module I've installed.
If I 'owned' Foo I could just do:
class Foo(models.Model):
users = models.ManyToMany(User)
Then, if I wanted to add a Foo to user, or vice versa I could do:
my_user.foo_set.add(my_foo)
my_foo.users.add(my_user)
But I don't 'own' either code, and I want to inject this relationship, so I can do the above.
Now, if I wanted I could even do a through relationship through a model I made and and put that on either side, but that still requires altering the models.
Now, behind the scenes it looks like django many-to-many relationships are models (they definitely have tables), is it possible to make this code:
class FooUserRelationship(models.Model):
foo = ForeignKey(Foo)
user = ForeignKey(User)
Act just like a many-to-many relationship?
you can use proxy models. Here person is a 3rd party model and Myperson is a your model modifying extra attributes to the main models. for more details https://docs.djangoproject.com/en/1.9/topics/db/models/#proxy-models
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
class MyPerson(Person):
users = models.ManyToMany(User)
class Meta:
proxy = True
The cleanest way i can think of is to provide a middle class (or you could subclass which would provide the almost the same queries) with a onetoone to one side and then many to many
class FooUser(models.Model):
user = OneToOneField(AUTH_USER_MODEL)
foo = ManyToMany(Foo)
my_user.foouser.foo_set.add(my_foo)
my_foo.foousers.add(my_user.foouser)
Now I admit that this isn't the cleanest way of doing things since it would involve a further SQL join when retrieving results, but it does keep in tact your link and provides a way to modify as you please.

Django User Model Methods

So I have Auth and Profile implemented into my system however i'd like to extend the User model and I don't know what is considered the 'correct' way of doing this.
I just want to create a method that returns a link for the user like:
Username
I figured that this is probably best done using a method as the link will change and I don't want to be going through all my template files fixing this.
This may be common to somebody using Django but I have not used it much so I am not well versed in the conventions so any advice would be great
If you're not altering what is stored in the database, the easiest way is a proxy model. Here's an example straight out of the documentation (https://docs.djangoproject.com/en/dev/topics/db/models/#proxy-models)
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
class MyPerson(Person):
class Meta:
proxy = True
def do_something(self):
# ...
pass
If you want to do something more complex than adding methods to the User model, such as adding new data fields, I would recommend creating a Model with a one-to-one relation to User.
The normal way to create URLs in Django without needing to worry about changing them is to use the {% url %} tag. See the documentation on reversing URLs.
I know you are asking specifically for extending the user model but have you considered creating a template tag to generate the link instead?
If you only need the method in the templates than that's definitely the way to go.

Setup Django Blog Comments

If I wanted to setup comments for a blog in Django, and I wanted people to be able to reply to comments (like a normal blog), meaning each comment would have to know if it's a comment on another comment or not, would I set the model fields like this?
from django.db import models
from django.contrib.auth.models import User
class Comment(models.Model):
post = models.ForeignKey(Post)
user = models.ForeignKey(User)
text = models.TextField()
date = models.DateTimeField()
reply_to = models.ForeignKey(Comment, blank=True, null=True)
Is that correct? And how would I display them in a template?
Writing a hierarchical comments application seems too easy at first look but believe me it is not that simple. There are too many edge cases and security issues. So if this is a real project i would suggest you to use disqus, any other hosted solution or (now deprecated) comments framework.
On the other hand if you are just trying to learn how things done or playing around, your code seems fair enough so far. But you should consider Django's built-in content types framework instead of a direct foreign key relationship. That way you can relate a comment object to any other object. (a blog post or another comment). Take a look at comment frameworks models.py and you will see it.
class BaseCommentAbstractModel(models.Model):
"""
An abstract base class that any custom comment models probably should
subclass.
"""
# Content-object field
content_type = models.ForeignKey(ContentType,
verbose_name=_('content type'),
related_name="content_type_set_for_%(class)s")
object_pk = models.TextField(_('object ID'))
content_object = generic.GenericForeignKey(ct_field="content_type", fk_field="object_pk")
Also take a look at RenderCommentListNodein comment framework template tags. You should write a recursive function in order to get and display hierarchical comments.
You have to consider cases like:
What will happen if a user deletes a comment?
How should we delete comments? Should we actually remove it from database or should we set an attribute like deleted
How should we deal with permissions and level of user access?
If we let anonymous users to comment, what information do we need from them.
How to check human validation? Is captcha enough?
Happy hacking.

How do I apply Django model Meta options to models that I did not write?

I want to apply the "ordering" Meta option to the Django model User from django.contrib.auth.models. Normally I would just put the Meta class in the model's definition, but in this case I did not define the model. So where do I put the Meta class to modify the User model?
This is how the Django manual recommends you do it:
You could also use a proxy model to define a different default ordering on a model. The standard User model has no ordering defined on it (intentionally; sorting is expensive and we don't want to do it all the time when we fetch users). You might want to regularly order by the username attribute when you use the proxy. This is easy:
class OrderedUser(User):
class Meta:
ordering = ["username"]
proxy = True
Now normal User queries will be unorderd and OrderedUser queries will be ordered by username.
Note that for this to work you will need to have a trunk checkout of Django as it is fairly new.
If you don't have access to it, you will need to get rid of the proxy part and implement it that way, which can get cumbersome. Check out this article on how to accomplish this.
Paolo's answer is great; I wasn't previously aware of the new proxy support. The only issue with it is that you need to target your code to the OrderedUser model - which is in a sense similar to simply doing a User.objects.filter(....).order_by('username'). In other words, it's less verbose but you need to explicitly write your code to target it. (Of course, as mentioned, you'd also have to be on trunk.)
My sense is that you want all User queries to be ordered, including in third party apps that you don't control. In such a circumstance, monkeypatching the base class is relatively easy and very unlikely to cause any problems. In a central location (such as your settings.py), you could do:
from django.contrib.auth.models import User
User.Meta.ordering = ['username']
UPDATE: Django 1.5 now supports configurable User models.
You can either subclass User:
class OrderedUser(User):
class Meta:
ordering = ['-id', 'username']
Or you could use the ordering in ModelAdmin:
class UserAdmin(admin.ModelAdmin):
ordering = ['-id', 'username']
# unregister user since its already been registered by auth
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
Note: the ModelAdmin method will only change the ordering in the admin, it won't change the ordering of queries.
Contact the author and ask them to make a change.

Categories

Resources