Django TabularInline fields giving error on updating inline fields - python

I have a models like
class Section(models.Model):
section_title = models.CharField(max_length=200)
section_tiles = models.ManyToManyField(Tile, blank=True)
on_menu = models.BooleanField(default=False)
section_is_active = models.BooleanField(default=True)
class Meta:
verbose_name = "Section"
verbose_name_plural = "Sections"
def __unicode__(self):
return self.section_title
class Page(models.Model):
page_title = models.CharField(max_length=200)
page_sections = models.ManyToManyField(Section, blank=True)
on_menu = models.BooleanField(default=False)
page_is_active = models.BooleanField(default=True)
class Meta:
verbose_name = "Page"
verbose_name_plural = "Pages"
def __unicode__(self):
return self.page_title
And at the admin part I have code such as:
class SectionTabularInline(admin.TabularInline):
model = Page.page_sections.through
class PageAdmin(admin.ModelAdmin):
inlines = [SectionTabularInline,]
list_display =[
'page_title',
'on_menu',
]
list_filter =[
'on_menu',
]
search_fields = [
'page_title',
]
ordering = ['page_title']
admin.site.register(Page, PageAdmin)
When I make changes in inline section of the page, Its giving an error "Please correct the errors below." without any additional information. If I remove sections from page and save it, then I can reassign them from scratch. Is there any way to change them without removing them first?

From: Save M2M "Through" Inlines in Django Admin
I found my problem: if you want many to many relationship you need to keep your original model's primary key(id).

Related

Django REST Framework, Serializers: Additional data?

Good day,
I would like to ask, if there's a possibility to gain additional data inside my serializers?
These are my models...
models.py
class Chair(models.Model):
name = models.CharField(max_length=100, null=False, blank=False, unique=True)
bookable = models.BooleanField(default=False)
user_created = models.CharField(max_length=100)
date_created = models.DateField(auto_now_add=True)
class Booking(models.Model):
chair = models.ForeignKey(Chair, on_delete=models.CASCADE)
day = models.DateField()
user_name = models.CharField(max_length=100)
user_created = models.CharField(max_length=100)
date_created = models.DateField(auto_now_add=True)
and these my serializers...
serializers.py
class BookingSerializer(serializers.ModelSerializer):
class Meta:
model = Booking
fields = '__all__'
class ChairSerializer(serializers.ModelSerializer):
class Meta:
model = Chair
fields = '__all__'
When making a request inside js like this...
views.py
#api_view(['GET'])
def bookings_by_date(request, pk):
bookings = Booking.objects.filter(day=pk)
serializer = BookingSerializer(bookings, many=True)
return Response(serializer.data)
script.js
let url = '...here's my url for Booking...';
fetch(url)
.then((resp) => resp.json())
.then(function(data) {
// do something here
});
...I would like to get not only the id of the Chair (models.Foreignkey), but also it's name. My first thought was doing something like this...
class ChairSerializer(serializers.ModelSerializer):
class Meta:
model = Chair
fields = [
...
'chair',
'chair__name',
...
]
...but this doesn't seem to work! Does anyone know a solution for my problem? Thanks for all your help and have a great weekend!
You can use one of this two ways:
1-) Using SerializerMethodField. You can add readonly fields with this way. You should add get_<field_name> method or give a method name that you want to run for this field with name keyword. You can look the document for more details.
class BookingSerializer(serializers.ModelSerializer):
chair__name = serializers.SerializerMethodField()
class Meta:
model = Booking
fields = '__all__'
def get_chair_name(self, obj):
return obj.chair.name
2-) Using CharField with source attribute:
You can define basically this field fill from where.
class BookingSerializer(serializers.ModelSerializer):
chair__name = serializers.CharField(source='chair__name')
class Meta:
model = Booking
fields = '__all__'

DRF post to model with a many-to-many field

I have the following models:
class Tag(TimeStampModel):
name = models.CharField(unique=True, max_length=100)
slug = models.SlugField(max_length=100, unique=True, blank=True)
featured = models.BooleanField(default=False, blank=True)
class Deal(VoteModel, models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='deals',
on_delete=models.CASCADE)
title = models.CharField(max_length=1024, blank=False, null=False)
slug = models.SlugField(max_length=1024, unique=True, blank=True)
description = models.TextField(blank=False, null=False)
poster = models.ImageField(blank=True)
tags = models.ManyToManyField(
Tag, blank=True)
And the following serializers:
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = ['id', 'name', 'slug', 'featured', 'created_at']
class DealSerializer(serializers.ModelSerializer):
user = UserSerializer(many=False, read_only=True)
tags = TagSerializer(many=True, read_only=True)
tags_ids = serializers.PrimaryKeyRelatedField(many=True, write_only=True, queryset=Tag.objects.all())
class Meta:
model = Deal
fields = '__all__'
Views
class DealList(viewsets.ModelViewSet, VoteMixin):
serializer_class = DealSerializer
permission_classes = [IsOwnerOrAdminOrReadOnly]
def get_queryset(self):
return Deal.objects.all()
def perform_create(self, serializer):
serializer.save(user=self.request.user)
I am able to get the data and also post it, but because of the many-to-many field (tags), I seem to have some issues as a Deal may have tags that only exist (created beforehand, and cannot be created through a post request to the Deal).
I send data as the following:
{
title: 'some title',
all_other_fields: 'some data',
tags_ids: [2, 4]
}
The tags are sent as an array of tag ids, but I get an error as the following:
"Incorrect type. Expected pk value, received str."
I only added the tags_ids so I could perform write operations on it as I couldn't figure out a way to use the field tags as both a read and write field that would return an object on read, and accept an id on write.
I have read through many posts here on Stackoverflow, but nothing that would work for me yet. Any help would be very appreciated. Thank you.
Try changing your serializer like this
class DealSerializer(serializers.ModelSerializer):
user = UserSerializer(many=False, read_only=True)
tags = serializers.PrimaryKeyRelatedField(many=True, queryset=Tag.objects.all())
class Meta:
model = Deal
fields = '__all__'
Try doing it this way.
class DealSerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=True)
tags = TagSerializer(read_only=True)
class Meta:
model = Deal
fields = ('user', 'title', 'slug', 'description', 'poster', 'tags')

Django Admin filter for another model inside edit form

I face the following problem.
I have 3 different Models:
1.) Testsuite (which contains a list of Tests)
2.) Test (which have different groups. e.g. "production","live", "frontend", "backend" , etc)
3.) Groups (list of all available groups.
The tester needs to create a test suite with a list of tests.
But adding them one by one is not suitable.
The better option is to add them in bulk sorted by groups.
I am looking for 2 solutions.
Include the filter option inside the edit form.
That filter here would be nice
or The other options.
The horizontal list in the edit form
is able to search for the Group tags.
Some code to get a better understanding.
in the forms.py:
<pre>
class TestSuiteForm(forms.ModelForm):
class Meta:
model = TestSuiteModel
fields = ('name','testcases' , 'nutzer' )
widgets = {
'testcases': autocomplete.ModelSelect2Multiple(
'Testcase_autocomplete'
)
}
class TestCaseForm(forms.ModelForm):
class Meta:
model = TestCaseModel
fields = ('name', 'testsuite' , 'gruppen' , 'portal' )
widgets = {
'testsuite': autocomplete.ModelSelect2Multiple(
'Testsuite_autocomplete'
),
'gruppen': autocomplete.ModelSelect2Multiple(
'Gruppen_autocomplete'
),
}
class GroupForm(forms.ModelForm):
class Meta:
model = GroupModel
fields = ('name', 'testcases' )
widgets = {
'testcases': autocomplete.ModelSelect2Multiple(
'Testcase_autocomplete'
)
}
</pre>
the admin.py
<pre>
class TestSuiteFormAdmin(admin.ModelAdmin):
search_fields = ('name',)
form = TestSuiteForm
list_filter = ['name']
class TestCaseAdmin(admin.ModelAdmin):
form = TestCaseForm
list_filter = ['gruppen', ]
list_display = ['name', ]
search_fields = ('name',)
class GroupAdmin(admin.ModelAdmin):
form = GroupForm
list_filter = ['name']
</pre>
and models.py
<pre>
class TestCaseModel(models.Model):
#id = models.CharField(primary_key=True, max_length=50)
name = models.CharField(max_length=50)
gruppen = models.ManyToManyField('GroupModel' , blank=True)
testsuite = models.ManyToManyField('TestSuiteModel' , blank=True)
def __str__(self):
return self.name
class Meta:
db_table = "Testcase"
verbose_name = 'Testcase'
verbose_name_plural = 'Testcases'
#python_2_unicode_compatible
class TestSuiteModel(models.Model):
name = models.CharField(max_length=200)
testcases = models.ManyToManyField('TestCaseModel' , blank=True)
nutzer = models.CharField(max_length=200, blank=True)
def __str__(self):
return self.name
class Meta:
db_table = "Testsuite"
verbose_name = 'Testsuite'
verbose_name_plural = 'Testsuits'
class GroupModel(models.Model):
name = models.CharField(max_length=50)
testcases = models.ManyToManyField('TestCaseModel', blank=True)
def __str__(self):
return self.name
</pre>
This was one solution I found. but that does not work with many to many Fields.
from pprint import pprint
from django.utils.encoding import smart_text
from django.utils.translation import ugettext_lazy as _
class CategoryListFilter(admin.SimpleListFilter):
title = _('TestCaseModel')
parameter_name = 'testcasemodel'
def lookups(self, request, model_admin):
categories = TestCaseModel.objects.all()
for obj in categories:
pprint(vars(obj))
#filter_objects = TestCaseModel.objects.filter(Some_attribut = "some_name")
#filter_objects = TestCaseModel.objects.filter(group = "backend")
def queryset(self, request, queryset):
if self.value():
return queryset.filter(testcasemodel__id=self.value())
class TAdmin(admin.ModelAdmin):
list_filter = (CategoryListFilter,)
For many-to-many relations, you get a none value and there you are not able to filter for that.

Filtering a django-tables2 table with django-filters using a drop down list

I have a model (Brief) that i am showing in a list with Django-tables2. A Brief can belong to one or many towers. I am trying to create a view where a user can see all briefs, and then filter to find briefs related to a specific tower. I had first implemented this using check boxes, and it was working, but I need to get that filter into a drop down list.
"GET /brief/?towers= HTTP/1.1" - Gives me all the briefs.
"GET /brief/?towers=1 HTTP/1.1" - Gives an empty list of briefs.(should give me 2 in my test data)
Querying in django shell gives me the results I would expect.
Another odd behavior is that my drop down list has spaces that can be selected.
class Attribute(models.Model):
class Meta:
abstract = True
ordering = ['name']
name = models.CharField(max_length=100, unique=True)
created_on = models.DateTimeField(auto_now_add=True)
created_by = models.ForeignKey(User, related_name='%
(class)s_created_by', null=True, blank=True,
on_delete=models.SET_NULL)
modified_dt = models.DateTimeField(auto_now=True)
modified_by = models.ForeignKey(User, related_name='%
(class)s_modified_by', null=True, blank=True,
on_delete=models.SET_NULL)
def __str__(self):
return self.name
class Tower(Attribute):
pass
class Brief (Attribute):
link = models.URLField()
sources = models.ManyToManyField(SourceSystem)
format = models.ForeignKey(ReportFormat, on_delete=models.PROTECT)
towers = models.ManyToManyField(Tower)
type = models.ForeignKey(ReportType, on_delete=models.PROTECT)
project = models.ForeignKey(Project, on_delete=models.PROTECT)
def tower_list(self):
return ", ".join([str(obj) for obj in self.towers.all()])
def source_list(self):
return ", ".join([str(obj) for obj in self.sources.all()])
My tables.py:
class BriefTable(tables.Table):
name = tables.LinkColumn('brief_detail', args=[A('pk')])
class Meta:
model = Brief
template_name = 'django_tables2/bootstrap.html'
sequence = ('id', 'name', 'type', 'project', 'format',)
exclude = ('link', 'created_on', 'created_by', 'modified_dt', 'modified_by', 'info')
My filters.py
class BriefFilter(django_filters.FilterSet):
towers=django_filters.ModelMultipleChoiceFilter(
queryset=Tower.objects.all(), widget=forms.Select)
class Meta:
model = Brief
fields = ['towers']
my view:
class FilteredBriefListView(LoginRequiredMixin, SingleTableMixin,
FilterView):
table_class = BriefTable
template_name = 'brief/brief_list.html'
paginate_by = 20
filterset_class = BriefFilter
finally my route:
urlpatterns = [
path('', FilteredBriefListView.as_view(), name="home"),
]
After taking a little break I realized I'm using ModelMultipleChoiceFilter with a single choice instead of ModelChoiceFilter... /facepalm.

Django why use inline in admin?

Just starting out with Django and I have a basic model for creating blog posts. Basic idea is I want each post to have multiple tags and hopefully later use those for filtering my posts.
class Tag(models.Model):
slug = models.SlugField(max_length=50, unique=True)
def __str__(self):
return self.slug
class Meta:
verbose_name = "Tag"
verbose_name_plural = "Tags"
class Post(models.Model):
post = models.TextField()
pub_date = models.DateTimeField('date published', auto_now=True)
title = models.CharField(max_length=100)
slug = models.SlugField(max_length=100, unique=True)
tags = models.ManyToManyField(Tag)
def __str__(self):
return self.title
class Meta:
verbose_name = "Blog Entry"
verbose_name_plural = "Blog Entries"
ordering = ["-pub_date"]
on my admin side I'm wondering the best way be able to add posts. I've been reading a bunch of threads where people suggest to use an inline style for manytomany relationships, but don't see the difference between that and just editing the object.
So why go with this
class TagInline(admin.TabularInline):
model = Post.tags.through
extra = 1
class newsAdmin(admin.ModelAdmin):
list_display = ('title', 'pub_date')
prepopulated_fields = {"slug": ("title",)}
inlines = [TagInline]
exclude = ('tags',)
admin.site.register(Post, newsAdmin)
admin.site.register(Tag)
over this?
class newsAdmin(admin.ModelAdmin):
list_display = ('title', 'pub_date')
prepopulated_fields = {"slug": ("title",)}
admin.site.register(Post, newsAdmin)
admin.site.register(Tag)
An inline has (almost?) no benefit over a regular field in the case of a default through model for a ManyToManyField. But consider the following (simplified) model:
STATUS = (
('pending', 'Pending'),
('accepted', 'Accepted'),
('rejected', 'Rejected'),
)
class Comment(models.Model):
post = models.ForeignKey(Post)
comment = models.TextField()
status = models.CharField(max_length=20, choices=STATUS)
The primary objective is no longer to connect the right comment to the right post. However, it would be very convenient to moderate all comments on a post on the edit page of the post itself, instead of moderating each comment on a separate page. An inline allows you to change the status of each comment on a single page.
Basically, if you just want to select which items to relate to your primary object, an inline isn't needed. If you need to frequently edit the objects related to your primary object, an inline provides a huge convenience.

Categories