Django OneToOneField to multiple models and multi OneToOneField - python

I have the following models:
class District(models.Model):
pk_district = models.AutoField(primary=True)
class Block(models.Model):
pk_block = models.AutoField(primary=True)
class Community(models.Model):
pk_community = models.AutoField(primary=True)
class RelateOne(models.Model):
pk_object = models.OneToOneField('District or Block or Community')
name = models.CharField()
class RelateTwo(models.Model):
pk_object = models.OneToOneField('District or Block or Community')
name = models.CharField()
I want the RelateOne or RelateTwo model to associate District or Block or Community, and then I can use it like this:
district = District.objects.get(pk=1)
district.relate_one.name
district.relate_two.name
block = Block.objects.get(pk=1)
block.relate_one.name
block.relate_two.name
Block.objects.select_related('relate_one','relate_two')
How should I set up the model correctly?

You can use GenericForeignKey from django docs: https://docs.djangoproject.com/en/3.2/ref/contrib/contenttypes/
I would specify the key in Block or District to ensure there is only "one" block per generic relation
Make sure to specify related attributes to get the reverse relationship, although in your case you don't need that.
so it would look something like this:
class Community(models.Model):
pk_community = models.AutoField(primary=True)
content_type = models.ForeignKey(ContentType,
on_delete=models.CASCADE)
object_id = models.CharField(max_length=100)
content_object = GenericForeignKey("content_type", "object_id")
class RelateOne(models.Model):
name = models.CharField(primary_key=)
class RelateTwo(models.Model):
name = models.CharField(primary_key=True)
# to use it would be something like this:
rel1= RelationOne(name="john")
rel2= RelationTwo(name="dave")
com_data={content_object=rel1,**your_other_data}
com=Community(**com_data)
com.save
#then to access relation:
com.content_object.rel_one_or_two_field....
# remember to be careful as these are generic, it's best to let a
#serializer take care of this.

Related

Add property in Django many to many relation

I have the following Django models:
class Lesson(models.Model):
title = models.TextField()
class Course(models.Model):
lessons = models.ManyToManyField(Lesson)
class User(AbstractUser):
favorites = models.ManyToManyField(Lesson)
I have a route /courses/course_id that returns a course details including an array of lessons (using Django Rest Framework)
How can i return in the lessons object an additional attribute favorite based on my users favorites.
I attempted the following:
course = self.get_object(course_id)
favorites = request.user.favorites
for lesson in course.lessons.all():
if lesson in favorites.all():
lesson.favorite = True
serializer = CourseDetailSerializer(course, context=serializer_context)
return Response(serializer.data)
But when returning it doesn't work:
(django.core.exceptions.ImproperlyConfigured: Field name favorite is
not valid for model Lesson.
My serializers:
class CourseDetailSerializer(serializers.HyperlinkedModelSerializer):
lessons = LessonListSerializer(many=True, read_only=True)
class Meta:
model = Course
fields = ('id', 'lessons', 'name', 'title')
class LessonSerializer(serializers.ModelSerializer):
class Meta:
model = Lesson
fields = ('id', 'title', 'duration', 'favorite')
You cannot add properties to objects if they are not defined, like here:
lesson.favorite = True
When you create m2m relation:
favorites = models.ManyToManyField(Lesson)
... django creates virtual model that simply stores pairs of primary keys from both models. This relation could look like this in database:
id | user_id | lesson_id
------+---------------+----------
151 | 11 | 3225
741 | 21 | 4137
What I think you want to achieve is to add extra information about this relation.
Therefore you need to create intermediary model with that extra field, i.e:
class User(AbstractUser):
favorites = models.ManyToManyField(Lesson, through='UserLessons')
class UserLessons(models.Model):
user = models.ForeignKey(User)
lesson = models.ForeignKey(Lesson)
favorite = models.BooleanField(default=False)
Your lessonmodel doesn't include a favourite boolean, so it isn't able to set it when you call lesson.favorite = True
If you want to get rid of the error, try:
class Lesson(models.Model):
title = models.TextField()
favorite = models.BooleanField(initial=False)
Although it appears that the lessons aren't user-specific. So this solution might not be what you are looking for, because it will set a Lesson's "favourite" field to be true for all users if only one user sets it as a favorite.

Insert multiple keywords in one field

My question is how i can insert multiple keywords in one django field and show them in a template like stackoverflow tags.
Models:
class Jobs(models.Model):
title = models.CharField(max_length=100)
slug = models.SlugField(blank=True, default='')
company = models.ForeignKey(Company, on_delete=models.CASCADE)
tags = ?????
Create another class and use many-to-many relationship between jobs class (tags) and new class:
class Tags(models.Model):
tag_name=models.CharField()
In jobs class
tags=models.ManyToManyField(Tags)
For show in template you can use for loop, etc.
Make it a Comma separated value.
class Jobs(models.Model):
tags = models.TextField()
def tag_list(self):
return self.tags.split(",")
def add_tag(self, tag_str):
current_tags = self.tag_list()
current_tags.append(tag_str)
current_tags = set(current_tags)
new_tag_string = ",".join(current_tags)
self.tags = new_tag_string
# you could save the model now or let caller save it outside of this method. I suggest letting caller save the model.
def remove_tag(self, tag_str):
current_tags = self.tag_list()
current_tags.remove(tag_str)
new_tag_string = ",".join(current_tags)
self.tags = new_tag_string
# you could save the model now or let caller save it outside of this method. I suggest letting caller save the model.

Django Access to Foreign Key data to set a field default value

I have two models with their respective forms. One has a Foreign Key link to the other and from, here I would like to set some fields default data.
class Lexicon(models.Model):
[...]
case_sensitive = models.BooleanField(default=True)
invariant = models.NullBooleanField(default=False)
diacritics = models.BooleanField(default=True)
[...]
class Meta:
verbose_name = "lexicon"
ordering = ["filename"]
def __str__(self):
return self.filename
class Lexeme(models.Model):
lexicon = models.ForeignKey(Lexicon, on_delete=models.CASCADE)
case_sensitive = models.BooleanField(default=True)
diacritics = models.BooleanField(default=True)
[...]
class Meta:
verbose_name = "lexeme"
I would like the Lexeme model fields "case_sensitive" and "diacritics" to default from Lexicon. I suppose the forms may be a better place to do this.
Any idea ?
As I understand, you only need to populate data from Lexicon to Lexeme model fields. You can override get_form_kwargs in your FormView as follows
def get_form_kwargs(self):
lex_obj = Lexeme.objects.get(pk=self.kwargs['pk'])
kwargs = super().get_form_kwargs()
kwargs['initial']['case_sensitive'] = lex_obj.lexicon.case_sensitive
kwargs['initial']['diacritics'] = lex_obj.lexicon.diacritics
return kwargs
Is that what you want? I have not tested but, I have used similar thing on my project. Let me know if works or not.
I finally found the way to go. It was just basic initial setting of field, no need to touch to forms.py, models.py nor the html template.
I passed data to my form like this:
lexeme_form = LexemeForm(initial={'case_sensitive': lexicon.case_sensitive, 'diacritics': lexicon.diacritics})
use Ajax at template to change the initial value of "case_sensitive" and "diacritics" when Lexicon changed, and abstract model can be used to reduce repeat lines :
class BaseLex(models.Model):
case_sensitive = models.BooleanField(default=True)
diacritics = models.BooleanField(default=True)
class Meta:
abstract = True
class Lexicon(BaseLex):
# without `case_sensitive` and `diacritics' fields
...
class Lexeme(BaseLex):
# without `case_sensitive` and `diacritics' fields
lexicon = models.ForeignKey(Lexicon, on_delete=models.CASCADE)
...

Dynamic field value in Django class

I want to create one dynamic field value for my class in Django using PyCharm.
CATEGORY_CHOICES = (
('on','one'),
('tw','two'),
('th','three'),
('fo','four'),
('fi','five'),
)
class art(models.Model):
Title=models.CharField(max_length=300)
Desciption=models.TextField()
Category=models.CharField(max_length=2, choices=CATEGORY_CHOICES)
I want the category field in my class to take more than one option, maybe two or more.
Any help would be appreciated.
If you want one python model to have multiple categories, then you need django ManyToManyField. Basically one model object could have multiple choices, one choice can also belong to multiple models objects:
class Category(models.Model):
category_name = models.CharField(max_length=10, unique=True)
class Art(models.Model):
title = models.CharField(max_length=300)
description = models.TextField()
category = models.ManyToManyField('Category', blank=True)
Note that I put unique=True for category_name to avoid creating duplicate categories.
Something not related, you shouldn't use lower fist in model name, and upper first for field name, that's really BAD naming convention and might confuse others who read your code.
Example:
# create your category in code or admin
one = Category.objects.create(category_name='one')
two = Category.objects.create(category_name='two')
three = Category.objects.create(category_name='three')
# create a new art obj
new_art = Art.objects.create(title='foo', description='bar')
# add category to Art obj
new_art.category.add(one)
new_art.category.add(two)
# category for new art obj
new_art_category = new_art.category.all()
# get only a list of category names
category_names = new_art_category.values_list('category_name', flat=True)
# create another Art obj
new_art2 = Art.objects.create(title="test", description="test")
# assign category to new_art2
new_art2.category.add(two)
new_art2.category.add(three)
Django doc for many to many and python pep8 doc.

Making a smart class factory in Django

I've been trying to figure this out for a while now with little success. I'm attempting to write a class factory that plays nice with Django's ORM, so that I can take a model schema like this:
Product
SubclassOfProduct0
SubclassOfProduct1
....
To work like this:
Product.objects.get(pk=7) // returns the result of SubclassOfProduct0(pk=7)
Product.objects.filter(propname="w00t") // returns a QuerySet of Product objects
So I was thinking something like this:
class ProductManager(models.Manager):
def get(self, *a, **kwa):
# Get the id from Products (somehow)
if product.type == Product.TYPE_SUBCLASS0:
return ProductSubClass0.objects.get(pk=kwa["pk"])
class Product(models.Model):
TYPE_SUBCLASS0 = 0
TYPE_SUBCLASS1 = 1
objects = ProductManager()
def __init__(self, *a, **kwa):
self.set_defaults()
def set_defaults(self):
pass
class ProductSubClass0(models.Model):
def set_defaults(self):
self.type == self.TYPE_SUBCLASS0
...but I don't know how to do it "right". Can someone shed some light here?
Django Tagging has a great example in the models.py as to how it figures out the content type of specific classes. I'm currently using the pattern in another module I developed with permissions.
You could just subclass your Product, as documented here: http://docs.djangoproject.com/en/1.2/topics/db/models/#model-inheritance
class OtherProduct(Product):
battery_life = …
Maybe also make Product an abstract base class if you don’t need to use it directly.
You could use entity framework with generic relations. For example, in models.py:
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
# Product
class Product(models.Model):
name = models.CharField(max_length=128)
pub_date = models.DateTimeField('date published', null=True)
productDescription = models.CharField(max_length=400)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
#Shirt Product type
class ShirtProduct(models.Model):
product = generic.GenericRelation(Product)
#Book Product type
class BookProduct(models.Model):
product = generic.GenericRelation(Product)
....
For search one product id, you can use this method in your ProductManager:
product = generic.GenericRelation(Product,
content_type_field='content_type_fk',
object_id_field='object_primary_key')
(reverse generic relations in the same section of djangoproject page)

Categories

Resources