How to write this "model" in Django? - python

I am currently using Django Users model.
Very simple. However, I'd like to add one feature: Adding friends!
I would like to create 2 columns in my table:
UID (the ID of the User)
friend_id (the ID of his friend! ...of course, this ID is also in Django's User model.
The UID-friend_id combination must be unique! For example, if my ID is 84, I cannot have two rows the same, because I can only subscribe to the same friend once.
Can anyone tell me if this is the right way to do it? Should I do some KEY relationship for the "friend_id", or should I leave it like this, as "IntegerField"?
class Friend(models.Model):
uid = models.ForeignKey(User)
friend_id = models.IntegerField(default=0)
Thank you

You should create a model that defines the relationship between two users, and then define two foreign-key fields, each one to a User. You can then add a unique constraint to make sure you don't have duplicates.
There is a article here explaining exactly how to do this: http://www.packtpub.com/article/building-friend-networks-with-django-1.0
The example model from that page:
class Friendship(models.Model):
from_friend = models.ForeignKey(
User, related_name='friend_set'
)
to_friend = models.ForeignKey(
User, related_name='to_friend_set'
)
def __unicode__(self):
return u'%s, %s' % (
self.from_friend.username,
self.to_friend.username
)
class Meta:
unique_together = (('to_friend', 'from_friend'), )

Related

How to restrict a Django model to a single one-to-one relation?

I am currently building a Django app that requires two different user types and I know that storing authentication information across multiple models/tables is a bad idea.
As such, I have created a User model to handle the authentication information (username, password, etc.). I have then created two different models, one for the buyers and one for the sellers, each with their own unique fields and one-to-one relationship to the User model.
Now, I thought this would work, but the problem is that it is still possible for a different buyer and seller to have the same User relation. How can I prevent this and restrict the User model to only a single one-to-one relation?
You can use unique_together to achieve your goal like this:
class Actor(models.Model):
"""An abstract base class for Buyer and Seller"""
# We define an id file in the abstract class
# this way the id is unique for Buyer and Seller
id = models.AutoField(primary_key=True)
class Meta:
abstract = True
class Buyer(Actor):
user_id = models.ForeignKey(User, ...)
class Meta:
unique_together = ('id', 'user_id',)
class Seller(Actor):
user_id = models.ForeignKey(User, ...)
class Meta:
unique_together = ('id', 'user_id',)
With this example, one Buyer is linked to only one user and one Seller is linked to only one user too.
you can add role in actor's model and make a pre_save signal before save user in buyer or seller model

Inserting data into a Django ManyToMany field with intermediate table

I have an issue that I am unable to solve. Here is the scenario: there is a retail business which sells cigarettes. This business can have many sites, but each site does not necessarily sell the same types of cigarettes. For this reason, each store owner can browse to a web portal and select the cigarette types that their site sells – these selections will act as columns for a future table. The trouble I am having is writing the data to a database because it is a ManyToMany field. From the data entry perspective, everything appears to be working. The user is able to select what they need and, as a test, I have a HttpResponse message that returns the correct value to the POST method of the view.
The problem that I’m running into is that I don’t know how to save this data in a table because the underlying table is part of a many-to-many relationship. When I try to save the data coming from the form, I get an error:
"Cannot assign "'6565'": "CigCountColumnsMappings.site_id" must be a "Sites" instance."
I have tried many things and I just can’t seem to get this to insert. I think this has something to do with the fact that site_id is a OneToOne/ForeignKey to the Sites model.
My models.py:
class Sites(models.Model):
SITE_STATUS = (
('Open', 'Open'),
('Closed', 'Closed'),
('Maintenance', 'Maintenance')
)
id = models.CharField(max_length=20, verbose_name='Site ID', primary_key=True)
def __str__(self):
return self.id
class CigCountColumns(models.Model):
column = models.CharField(max_length=10, primary_key=True, db_column="column", verbose_name="Column")
def __str__(self):
return str(self.column)
class Meta:
ordering = ["column", ]
verbose_name = "Cigarette Count Column"
verbose_name_plural = "Cigarette Count Columns"
class CigCountColumnsMappings(models.Model):
site_id = models.OneToOneField('home.Sites', on_delete=models.CASCADE, primary_key=True, db_column="site_id", verbose_name="Site ID")
columns = models.ManyToManyField(CigCountColumns, db_column="columns", verbose_name="Cig. Count Column Mappings", blank=True)
def __str__(self):
return str(self.site_id)
class Meta:
ordering = ["site_id", ]
verbose_name = "Cig. Count Column Mapping"
verbose_name_plural = "Cig. Count Column Mappings"
My views.py:
def cigarette_columns(request):
if request.method == "POST":
this_site = request.POST['this_site']
choices = request.POST.getlist('choices')
for choice in choices:
record = CigCountColumnsMappings.objects.create(site_id=this_site, columns=choice)
record.save()
if choices:
return HttpResponse([this_site, choices])
else:
return HttpResponse([this_site, "No data entered"])
The fact that site_id has a reference to the “home.Sites” table is messing me up. I need to insert the values directly into the CigCountColumnsMappings model to map site 6565 to those particular columns but I am unable to do so. I have taken a look at “through” models and read lots of documentation on ManyToMany fields but the solution still eludes me. Many thanks in advance for any help.
First, change site_id to site. This will automatically create a field named site_id that is an integer value for the primary key.
Second, columns is a reference to CigCountColumns, but you assign it as choices which is a list. You need to create a CigCountColumns instance or get one from the database or have the id for a CigCountColumns before you create a CigCountColumnsMappings.
Just as an FYI, I was unable to resolve this issue in a pythonic Django way. I ended up using Django raw SQL queries to hit the table directly (Google: "from django.db import connection") and this resolved it. It's definitely not as "clean" as using the built-in Django methods, but it's not as dirty as completely bypassing Django and using pymysql either.

Trying to extend AbstractUser to create multiple user types in Django

So I have been searching all around the internet for a full example of how to user AbstractUser when u have at least 2 different models. Didn't find anything conclusive.. at least that would work on latest version of Django (2.0.1).
I have 2 models, teacher and student, and registration needs to be different. Besides username, email, name and surname, I need for example, for the student, to upload a profile picture, email, phone, student_ID. And for teacher, bio, academic title and website. Did I start good ? What is the right approach ?
class Profile(AbstractUser):
photo = models.ImageField(upload_to='students_images')
email = models.EmailField()
phone = models.CharField(max_length=15, )
class Student(Profile):
student_ID = models.CharField(unique=True, max_length=14,
validators=[RegexValidator(regex='^.{14}$',
message='The ID needs to be 14 characters long.')])
def __str__(self):
return self.name
class Teacher(Profile):
academic_title = models.CharField(max_length=30)
bio = models.TextField()
website = models.URLField(help_text="E.g.: https://www.example.com", blank=True)
Your goals can be accomplished using a 'Profile' pattern. You don't necessarily need to use a custom user model for this. But you need to have a single common model to for authentication; you can use the builtin django user for this or a custom class... Your Student and Teacher models should be OnetoOne relationships. This is the recommended solution per the documentation.
If you wish to store information related to User, you can use a OneToOneField to a model containing the fields for additional information. This one-to-one model is often called a profile model, as it might store non-auth related information about a site user.
In your case, you may do something like this:
class StudentProfile(models.Model):
user = models.OneToOneField('User', related_name='student_profile')
# additional fields for students
class TeacherProfile(models.Model):
user = models.OneToOneField('User', related_name='teacher_profile')
# additional fields for teachers
Then you can create your registration forms based on these profile models.
class StudentResistrationForm(forms.ModelForm):
class Meta:
model = StudentProfile
fields = (...)
class TeacherRegistrationForm(forms.ModelForm):
class Meta:
model = TeacherProfile
fields = (...)
You can create the user instance to which the profile is related to at the same time you create the profile. You might do this with formsets, for example.
add
class Meta:
abstract = True
to profile model
and change AbstractUser to models.Model

Customize related field in Django model specifying foreign keys to relate through

In my Django project I have a model for products that look like this:
class Manufacturer(models.Model):
name = models.CharField(max_length=100)
class Product(models.Model):
manufacturer = models.ForeignKey('Manufacturer')
# .favorite_set: ManyToOne relation coming from the
# 'Favorite' class (shown a couple of lines below)
My site's User(s) can mark some products as Favorite. To provide this functionality, I have a Django model that looks like this:
class Favorite(models.Model):
user = models.ForeignKey(User)
product = models.ForeignKey('Product')
class Meta:
unique_together = ('user', 'product',)
In that model, the .product ForeignKey creates a reverse relation in the Product model called favorite_set. That's all good and useful: When I get an HTTP request from a user to retrieve products, I can easily figure out whether it's been favorited by a particular user or not by doing this:
product = Product.objects.get(id='whatever_id')
is_favorited = bool(product.favorite_set.filter(user=self.user).count() == 1)
# or probably:
# is_favorited = product.favorite_set.filter(user=self.user).exists()
#
Now, I have another model that is heavily denormalized (SQL denormalized, that is) that I want to use for fast text searches.
This model "pretends" to be a Product, but includes data found through the "regular" Product's FK relationships into the model itself. Something like this:
class ProductSearch(models.Model):
product = models.OneToOneField('Product',
on_delete=models.CASCADE,
related_name='searcher')
product_name = models.CharField(max_length=100)
manufacturer_name = models.CharField(max_length=100)
This class has its own id field (since it's a Django model) and, as you can see above, it is going to have a OneToOne relationship to the products (one of this ProductSearch entries is linked to one and only one Product)
Thanks to this model, if I want to search products whose manufacturer is "Ford" (for example), I don't need to join the Product table with the Manufacturer's table. I can do the lookup directly in ProductSearch and save a few milliseconds.
Since the ProductSearch is intended to be compatible with a Product, I'm also trying to model the favorite_set that occurs "naturally" in my Product class into this ProductSearch model.
And that's where the difficulties arise: I don't know how to do that :-)
I ideally would have something like:
class ProductSearch(models.Model):
product = models.OneToOneField('Product',
on_delete=models.CASCADE,
related_name='searcher')
manufacturer_name = models.CharField(max_length=100)
#
# Couldn't find anything to do the following:
product_favorite_set = models.ManyToOneField('Favorite',
through_fields=('product',))
But I haven't been able to do that.
I have tried to "abuse" the ManyToManyField like this:
class ProductSearch(BaseModel):
product = models.OneToOneField('Product',
on_delete=models.CASCADE,
related_name='searcher')
product_name = models.CharField(max_length=100)
manufacturer_name = models.CharField(max_length=100)
product_favorite_set = models.ManyToManyField('Favorite', related_name='+',
through='Favorite',
through_fields=['product']
)
But that produces an error on System Check:
api.Favorite: (fields.E336) The model is used as an intermediate model
by 'api.ProductSearch.product_favorite_set', but it
does not have a foreign key to 'ProductSearch' or 'Favorite'.
api.ProductSearch.product_favorite_set: (fields.E339) 'Favorite.product'
is not a foreign key to 'ProductSearch'.
I imagine I could make the product_favorite_set a Python #property, and then do a custom query in it like:
class ProductSearch(BaseModel):
# ....
#property
def product_favorite_set(self):
return Favorite.objects.filter(product=self.product)
But I would like to know if I can do this using "pure" Django tools (only if out of curiosity)
Any help would be greatly appreciated. Thank you in advance.

Django: allow user to add fields to model

I am just starting with Django and want to create a model for an application.
I find Djangos feature to
- automatically define validations and html widget types for forms according to the field type defined in the model and
- define a choice set for the field right in the model
very usefull and I want to make best use of it. Also, I want to make best use of the admin interface.
However, what if I want to allow the user of the application to add fields to the model? For example, consider a simple adress book. I want the user to be able to define additional atributes for all of his contacts in the admin settings, i.e. add a fax number field, so that a fax number can be added to all contacts.
from a relational DB perspective, I would have a table with atributes (PK: atr_ID, atr_name, atr_type) and an N:N relation between atributes and contacts with foreign keys from atributes and contacts - i.e. it would result in 3 tables in the DB. right?
but that way I cannot define the field types directly in the Django model. Now what is best practice here? How can I make use of Djangos functionality AND allow the user to add aditional/custom fields via the admin interface?
Thank you! :)
Best
Teconomix
i would suggest storing json as a string in the database, that way it can be as extendable as you want and the field list can go very long.
Edit:
If you are using other damn backends you can use Django-jsonfield. If you are using Postgres then it has a native jsonfield support for enhanced querying, etc.
Edit 2:
Using django mongodb connector can also help.
I've used this approach, first seen in django-payslip, to allow for extendable fields. This provides a structure for adding fields to models, from which you can allow users to add/edit through standard view procedures (no admin hacking necessary). This should be enough to get you started, and taking a look at django-payslip's source code (see the views) also provides view Mixins and forms as an example of how to render to users.
class YourModel(models.Model):
extra_fields = models.ManyToManyField(
'your_app.ExtraField',
verbose_name=_('Extra fields'),
blank=True, null=True,
)
class ExtraFieldType(models.Model):
"""
Model to create custom information holders.
:name: Name of the attribute.
:description: Description of the attribute.
:model: Can be set in order to allow the use of only one model.
:fixed_values: Can transform related exta fields into choices.
"""
name = models.CharField(
max_length=100,
verbose_name=_('Name'),
)
description = models.CharField(
max_length=100,
blank=True, null=True,
verbose_name=_('Description'),
)
model = models.CharField(
max_length=10,
choices=(
('YourModel', 'YourModel'),
('AnotherModel', 'AnotherModel'), # which models do you want to add extra fields to?
),
verbose_name=_('Model'),
blank=True, null=True,
)
fixed_values = models.BooleanField(
default=False,
verbose_name=_('Fixed values'),
)
class Meta:
ordering = ['name', ]
def __unicode__(self):
return '{0}'.format(self.name)
class ExtraField(models.Model):
"""
Model to create custom fields.
:field_type: Connection to the field type.
:value: Current value of this extra field.
"""
field_type = models.ForeignKey(
'your_app.ExtraFieldType',
verbose_name=_('Field type'),
related_name='extra_fields',
help_text=_('Only field types with fixed values can be chosen to add'
' global values.'),
)
value = models.CharField(
max_length=200,
verbose_name=_('Value'),
)
class Meta:
ordering = ['field_type__name', ]
def __unicode__(self):
return '{0} ({1}) - {2}'.format(
self.field_type, self.field_type.get_model_display() or 'general',
self.value)
You can use InlineModelAdmin objects. It should be something like:
#models.py
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=100)
class ContactType(models.Model):
name = models.CharField(max_length=100)
class Contact(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
contact_type = models.ForeignKey(ContactType, on_delete=models.CASCADE)
value = models.CharField(max_length=100)
#admin.py
from django.contrib import admin
class ContactInline(admin.TabularInline):
model = Contact
class PersonAdmin(admin.ModelAdmin):
inlines = [
ContactInline,
]
By the way... stackoverflow questions should contain some code. You should try to do something before asking a question.

Categories