Create Dynamic Through Model - django

I have many models with ManyToMany fields.
class Author(Model):
name = CharField()
class Publication(Model):
name = CharField()
authors = ManyToManyField(Author)
class Meta:
abstract = True
class Book(Publication):
pass
class Article(Publication):
pass
class Journal(Publication):
pass
class D(Model):
authors = ManyToManyField(Author)
class E(Model):
authors = ManyToManyField(Author)
I want to add ordering field to all ManyToMany fields.
What is the best way to automatically do this?
Attempt.
# Substitution for ManyToMany
class AuthorField(ManyToManyField):
def __init__(self, **kwargs):
class Relationship(Model):
entity = models.ForeignKey(???????????)
author = models.ForeignKey(Author)
ordering = models.PositiveSmallIntegerField(default=1)
class Meta:
ordering = ('ordering',)
kwargs['to'] = Author
kwargs['through'] = Relationship
super(AuthorField, self).__init__(**kwargs)

I have found a way.
class OrderedManyToManyField(models.ManyToManyField):
def contribute_to_class(self, cls, name, **kwargs):
super(OrderedManyToManyField, self).contribute_to_class(cls, name, **kwargs)
if self.remote_field.through:
self.remote_field.through.add_to_class('ordering', models.PositiveSmallIntegerField(default=1))
self.remote_field.through._meta.ordering = ('ordering', )

Related

Multiple models with similar fields Django

Say I have 2 models:
User
Customer
They both have the following SHARED fields:
First name
Last name
Pin code
Id
They also have a shared save() method:
def save(self, *args, **kwargs):
if not self.pk:
id = secrets.token_urlsafe(8)
while User.objects.filter(id=id).count() != 0:
id = secrets.token_urlsafe(8)
self.id = id
super(User, self).save(*args, **kwargs)
How could I create a Base model that they can extend so that I don't need to define all of these things twice? Thanks!!
You can make an abstract base class [Django-doc] that implements the common logic, and then inherit:
class MyBaseClass(models.Model):
id = models.CharField(max_length=8, primary_key=True)
first_name = models.CharField(max_length=128)
last_name = models.CharField(max_length=128)
pin_code = models.CharField(max_length=128)
def save(self, *args, **kwargs):
model = self._meta.model
if not self.pk:
id = secrets.token_urlsafe(8)
while model.objects.filter(id=id).exists():
id = secrets.token_urlsafe(8)
self.id = id
super().save(*args, **kwargs)
class Meta:
abstract = True
class User(MyBaseClass):
pass
class Customer(MyBaseClass):
pass
An abstract class will thus not construct a table, it basically is used to inherit fields, methods, etc. to avoid rewriting the same logic twice.

DRF ListSerializer and ListField

I use django rest in my project and until now for list of objects I used ListSerializer, when I needed to have min length and max length of list I googled and reached to ListField.
Before that my code worked fined without any error and misbehavior. Now I use ListField for my list field serializer, But I didn't get when to use ListSerializer? Can someone explain the difference between ListSerializer and FieldSerializer?
My sample code with ListSerializer:
tags = serializers.ListSerializer(child=serializers.CharField(allow_blank=False), required=False)
My sample code with ListField:
open_hour = serializers.ListField(child=serializers.DictField(), max_length=7, min_length=7)
Disclaimer: This answer is not complete
Can someone explain the difference between ListSerializer and
FieldSerializer?
I assume the question is difference between serializers.ListSerializer and serializers.ListField
Suppose we have two models as
class Musician(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
instrument = models.CharField(max_length=100)
def __str__(self):
return f'{self.first_name} {self.last_name}'
class Album(models.Model):
artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
release_date = models.DateField()
num_stars = models.IntegerField()
def __str__(self):
return f'{self.name} : {self.artist}'
and serializer as
class AlbumSerializer(serializers.ModelSerializer):
artist = serializers.StringRelatedField()
class Meta:
fields = '__all__'
model = Album
class MusicianSerializer(serializers.ModelSerializer):
AlbumSerializer(many=True, source='album_set')
class Meta:
fields = '__all__'
model = Musician
ListSerializer
As stated in official DRF doc
When a serializer is instantiated and many=True is passed, a
ListSerializer instance will be created. The serializer class then
becomes a child of the parent ListSerializer
For example, we could re-write the MusicianSerializer with ListSerializer as
class MusicianSerializer(serializers.ModelSerializer):
albums = serializers.ListSerializer(child=AlbumSerializer(), source='album_set')
class Meta:
fields = '__all__'
model = Musician
it would produce the results same as before. But, if we are trying to use ListField instead of ListSerializer It will raise an error
'RelatedManager' object is not iterable
When I checked the source code, I found that both ListSerializer and ListField are inherited from the same class (parent and grand parent are same)
I ran into this same problem and I believe I found a solution!
The trick is you need to create a new Serializer that inherits the ListSerializer class and override the to_representation() method to output your desired format.
If you look at the DRF source code for ListSerializer you can see the default to_representation() method looks like the following...
def to_representation(self, data):
"""
List of object instances -> List of dicts of primitive datatypes.
"""
# Dealing with nested relationships, data can be a Manager,
# so, first get a queryset from the Manager if needed
iterable = data.all() if isinstance(data, models.Manager) else data
return [
self.child.to_representation(item) for item in iterable
]
Example
models
class Musician(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
instrument = models.CharField(max_length=100)
def __str__(self):
return f'{self.first_name} {self.last_name}'
class Album(models.Model):
artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
release_date = models.DateField()
num_stars = models.IntegerField()
def __str__(self):
return f'{self.name} : {self.artist}'
serializers
class AlbumSerializer(serializers.ModelSerializer):
artist = serializers.StringRelatedField()
class Meta:
fields = '__all__'
model = Album
class AlbumKeyValueSerializer(serializers.ListSerializer):
def to_representation(self, data):
reaction_count_set = {}
for item in data.all():
reaction_count_set[item.name] = item.artist
return reaction_count_set
class MusicianSerializer(serializers.ModelSerializer):
AlbumKeyValueSerializer(child=AlbumSerializer(), source='album_set')
class Meta:
fields = '__all__'
model = Musician

django manytomany field using through and formwizard

I am trying to create a pretty complicated form and break it up using formwizard. The first thing I am trying to do is get the ManyToManyField using through to display, Then I need to figure out how to make it all save.
#models.py
----------------------
class Meat(models.Model):
name = models.charField(max_length=200)
company = models.CharField(max_length = 200)
class Starch(models.Model):
name = models.CharField(max_length=200)
company = models.CharField(max_length=200)
class Recipe(models.Model):
name = models.CharField(max_length=200)
description = models.TextField(help_text='Please describe the finished dish')
meat = models.ManyToManyField('Meat' through='RecipeMeat')
meat_notes = models.TextField()
starch = models.ManyToManyField('Starch' through='RecipeStarch')
starch_notes = models.TextField()
class RecipeMeat(models.Model):
recipe = models.ForeignKey(Recipe)
meat = models.ForeignKey(Meat)
qty = models.FloatField()
class RecipeStarch
recipe = models.ForeignKey(Recipe)
starch = models.ForeignKey(Starch)
qty = models.FloatField()
.
#forms.py
-------------------
class RecipeForm(forms.ModelForm):
class Meta:
model = Recipe
fields = ('name', 'description')
class RecipeMeatForm(forms.ModelForm):
class Meta:
model = RecipeMeat
class RecipeMeatNotesForm(forms.ModelForm):
class Meta:
model = Recipe
fields = ('meat_notes',)
class RecipeStarch(forms.ModelForm):
class Meta:
model = RecipeStarch
class RecipeStarchNotesForm(forms.ModelForm):
class Meta:
model = Recipe
fields = ('starch_notes')
MeatFormSet = inlineformset_factory(Recipe, RecipeMeat, form=RecipeMeatForm, extra=1)
.
#views.py
---------------------------
class CreateRecipeWizard(SessionWizardView):
template_name = "create-recipe.html"
instance = None
file_storage = FileSystemStorage(location= 'images')
def dispatch(self, request, *args, **kwargs):
self.instance = Recipe()
return super(CreateRecipeWizard, self).dispatch(request, *args, **kwargs)
def get_form_instance( self, step ):
return self.instance
def done( self, form_list, **kwargs ):
self.instance.save()
return HttpResponseRedirect(reverse(all-recipes))
.
#urls.py
------------------------------
url(r'^create-recipe/$', views.CreateRecipeWizard.as_view([RecipeForm, MeatFormSet, RecipeMeatNotesForm, RecipeStarchNotesForm]), name='create-recipe'),
.
I am a bit of a rookie with this django stuff. The Recipe part is much longer and more complicated but pretty much the same pattern. If any one could help point me in the right on how to get my ManyToManyField using through part figured out or pointed in the right direction it would be greatly appreciated.
To save the ManyToMany relationship on a formwizard process you can do something like this;
def done(self, form_list, **kwargs):
form_data_dict = self.get_all_cleaned_data()
m2mfield = form_data_dict.pop('m2mfield')
instance = form_list[0].save()
for something in m2mfield:
instance.m2mfield.add(something)
return render_to_response(
'done.html', {},
context_instance=RequestContext(self.request)
)
In this example the first form in the list is a ModelForm for the thing I'm trying to create and it has a ManyToManyField to another model for which I have a form second in the process. So I grab that first form & save it, then grab the field from the cleaned data from the second form and save the selected options to the M2M field.

Ordering Choices in ModelForm ManytoManyField DJANGO

I have in my models.py
class Business(models.Model):
industry = models.models.ManyToManyField(Industry)
in forms.py
class BusinessForm(forms.ModelForm):
class Meta:
model = Business
When I render the form, the industry names appear in a multiple select box. What do I do to make the industry names in alphabetical order?
There are several ways:
You can override the queryset ordering on a per-form basis, set the ordering meta class option, or override the model manager queryset with an ordering method.
Override global model manager queryset
class IndustryManager(models.Manager):
def get_query_set(self):
return (
super(IndustryManager, self)
.get_query_set()
.order_by('name')
)
class Industry(models.Model):
name = models.CharField(max_length=128)
objects = IndustryManager()
Specify global meta option ordering
class Industry(models.Model):
name = models.CharField(max_length=128)
class Meta:
ordering = ['name']
Per form ordering
class MyForm(forms.ModelForm):
class Meta:
model = Business
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['industry'].queryset = Industry.objects.order_by('name')
There's also a shortcut called formfield_for_manytomany if you are dealing with the django admin.
I like this method:
class BusinessForm(forms.ModelForm):
class Meta:
model = Business
industry = forms.ModelMultipleChoiceField(
queryset=Industry.objects.order_by('name'))
I like it, since it does not alter the database model, and since it is declarative (less programming).

How to order the results of a ForeignKey relationship in a Django form?

I have this models in Django
class Country(models.Model):
name = models.CharField(max_length=80)
class Person(models.Model):
first_name = models.CharField(max_length=100, db_index=True)
last_name = models.CharField(max_length=100, db_index=True)
country = models.ForeignKey(Country)
and this ModelForm
class PersonForm(forms.ModelForm):
class Meta:
model = Person
when I use this form in a template, everything works fine, but the country list in the <select> appears disordered. How can I order it?
You can use the ordering property:
class Country(models.Model):
name = models.CharField(max_length=80)
class Meta:
ordering = ["name"]
If you set the ordering to the Country class, it shall display them as you want.
If you can't or don't want to use the ordering attribute in class Meta of model, you also can do this:
You need make a Form object, something like:
from django import forms
class PersonForm(forms.ModelForm):
country = forms.ModelChoiceField(queryset=Country.objects.all().order_by('name'))
class Meta:
model = Person
field types for formsmodels
There's 2 good answers here, but I wanted to retain help_text, blank, and other settings from the model without having to repeat them and also not change the default ordering on the model. Here's what I did:
class PersonForm(forms.ModelForm):
class Meta:
model = Person
def __init__(self, *args, **kwargs):
super(PersonForm, self).__init__(*args, **kwargs)
self.fields['country'].queryset = self.fields['country'].queryset.order_by('name')
Essentially I just updated the queryset on the automatically added field to order the way I wanted.
try adding this into class Meta, inside class Person:
ordering = ['country']
http://docs.djangoproject.com/en/dev/ref/models/options/#ordering
In view.py
First: you create the form
form = YourForm(request.POST)
Later your set the query:
form.fields['country '].queryset = YourDBTable.objects.all().order_by('Your_Attr')