Change Django array field size depending on another field - python

I am building an app that displays different courses. For each course there is a list of the different classes it has.
Some courses have two classes, some have eight, some have just one or in the future some courses may have 10 classes. It's up to the app's administrator when they register a new course
class Curso(models.Model):
clases = models.IntegerField(default=1)
content = ArrayField(models.CharField(max_length=30),blank=False)
This model will only be for the administrator.
I want to store the different classes (just their names) in an array. But there's no need to show 8+ fields if the admin is just going to fill one in... Or is that the correct approach?
I want the admin to have an integer field where she types in how many classes the course has and depending on that the array fields will be displayed.
I understand ArrayField has a size attribute where I can say how long the array is. Right? So my question is:
Is there a way to dynamically change the array size depending on what the admin types in in the "clases" field?
I need this to work in the admin app. I am new to Django and I'm finding the admin app a little bit hard to manipulate.

Well I did my research. And this is how it turned out:
models.py
from django.db import models
from django_better_admin_arrayfield.models.fields import ArrayField
# Create your models here.
class Course(models.Model):
...
clases = ArrayField(models.CharField(max_length=10),null=True,blank=True, size=8)
...
Turns out I just needed to use ArrayField with django-better-admin-arrayfield
It doesn't do exactly what I described in this question, but it works just as fine (even better) to visually edit the arrayfield in the admin page
Here's the repository django-better-admin-arrayfield
You just need to pip install it and then add it to your settings.py installed apps
INSTALLED_APPS = [
...
'django_better_admin_arrayfield',
...
In admin.py
from django.contrib import admin
from .models import Course
from django_better_admin_arrayfield.admin.mixins import DynamicArrayMixin
#admin.register(Course)
class CursoAdmin(admin.ModelAdmin, DynamicArrayMixin):
...
And that was it! makemigrations and migrate and... It didn't work. The problem was the add another button wasn't working. So I read the issues in the repo and someone wrote you should run collectstatic and it worked!!
Shout out to #nbeuchat, whoever that is, that who's answer in in this stackoverflow post was the only thing that helped me get out of this problem

Related

Django Structures: Many apps VS one large app

I hope this is not a opinion based question but more like a solutions to a complicated django website. fyi, i am quite a beginner in django.
first of all, i am doing a complicated accounting django website where there are features like:
purchases (purchase order, quotation request, quotation, invoice, do)
Custom user roles and permissions, because we want user to setup their own roles and permissions system
sales (POS, stocks, too many to mentions)
user and registrations
incomes & expenses tracking and reporting.
There are articles and SO's answers about 'many apps vs 1 large app'. My confusion started. I figured out django allows seperating views.py and models.py into multiple file in app/views and app/models with init.py imports.
I personally do not like large app file as it is hard to locate things. I prefer neat structures. But the confusion keeps attacking. I want to do one thing and do it well but it seem like having one large app makes more sense because all the mentioned features are ForeignKey dependent.
So, according to your experience. what's your ideal folder structures and solutions to deal with this?
If you could provide performance difference that would be helpful.
UPDATE:
Since most people said multi apps, I have last question regarding this.
Since Django app can have models/ & views/ folder with multiple models.py & views.py inside, that means one large app can be seperated into multiple views files inside a views/ folder. What yall think about this?
since this will put all migrations in one place, does it provide long term safety in term of messy things like foreign keys across apps.
This kind of question I have asked long time back, on different platform. Common answer is
If you want to reuse that, then create an app or if you don't want
then you no need to make a separate app
Let me give you an example. If your project has features like
Sharing an Image
Sharing text
Comment on Media and Text
Upvote and Downvote options
Here an user can share either a image or text and other can comment on it or Upvote/Downvote it. In this case If you make Comment a separate app and Upvote/Downvote a separate app, then in future if you have to add Video along with Image and Text, then all you have to link Video to Comment app and Upvote/DOwnvote app, that will be less task comparing to a big app with all things inside. Also You can manage your database accordingly.
In your case, you can make (for example),
Custom User (help you add extra permission in future)
Product Category (help you to add more category or subcategory)
Products or Sales etc.
For many admins, when you add those apps to your settings.py, and if you edit admin.py separately in each app, everything will appear your admin panel without any hassle.
For many views, you can import all the models to views.py of any app or use it separately in their apps. It will not cause any problem. Same for urls.py and forms.py etc.
For your solution, i will recommend that you create multiple apps, with each app containing it's own view.py, models.py and urls.py. What you can do is, create a separate app for user that will contain User model (if you decide to override the existing user model provided by django) and all user related views (such as login, registration etc.). I will recommend that you create a separate model for Role as well that will contain all roles in your system. Make a management command that will add roles in that table, whenever you decide to add a new role. Create an enum type class like:
class Role(enum.Enum):
ADMIN = 1
USER= 2
labels = {
ADMIN: "Admin",
USER: "User"
}
So the first entry in Role table will be Admin with pk=1 and so on.
This was an idea as to how you can create models and keep things separate. You can associate user from a separate app to a table in your sales app as that won't cause you any problem and keep the code readable and things neat.
For your own sanity go for multiple apps.
just lay them out first and figure out what should logically go together in one app and what should go to the next.
You want to avoid running into circular imports!
Foreign keys across apps is no problem at all. Basically as soon as u use the USER anywhere in your models, you'll reference across app boundaries already.

Python django admin: How can I show only items belonging to specific model in an admin page?

I'm learning django for building my website.
I learned how to create models, views, but now I have some question relative to admin interface.
So I have tis code:
from django.db import models
class AudioBook(models.Model):
titolo=models.CharField(max_length=250)
capitoli=models.ManyToManyField('Chapters')
class Chapters(models.Model):
capitolo=models.CharField(max_length=250)
Now, when I add a new audiobook I can see chapters previously added to others audiobooks. This is bad. I want only the chapters taht belong to this audiobook, so, when I add a new one, the list must be empty.
I tried some things: adding limit_choices_to = models.Q(audiobook__titolo=titolo), but it doesn't work.
In command line I can retrieve these info by adding filters on Chapters object, but I can reproduce this situation in admin interface.
Any idea? I searched on google but I didn't find anything that helps me.
Germano
For ManyToMany relationships, Django admin interface allows you to see the entire list of choices you have and then you select whichever you want to add to your AudioBook object.
It does not mean all of them belong to that audio book. The selected ones (the ones you added to you audio books) will have normally a grey background
If that interface bothers you that much, I suggest that you add AudioBook foreign key in Chapters instead of your MAnyToMany relationship:
class Chapters(models.Model):
capitolo=models.CharField(max_length=250)
audio_book = models.ForeignKey(AudioBook,on_delete=models.CASCADE)
It's more relevent in your case. You still can access the audio book's chapters as easily.

Is it possible to filter choices on a foreign key in Django Admin panel?

Here is my situation:
I have three models : Grade, Course and Program.
Grade has a foreign key to Course and Course has a foreign key to Program.
Here is my problem:
When I need to add some grades in my admin panel, I have a listbox full of Course. First problem : same course name can be found in two different program and it is difficult to identify in the listbox. For now it is shown as Program.name - Course.name
I was wondering if there was any solution that can help me to filter my course list by program when I want to create or modify a grade. (I'm talking about the grade creation interface, not the list before that interface).
EDIT:
Thanks for your answers. I think I'll have to code my own widget make my filter client-side with AJAX.
You could write such a filter with AJAX (using jQuery or a similar framework would be the easiest way to do so). You'd create a custom form widget with two lists. The first would be populated with all available programs by the server when the page is loaded. Your javascript would then wait for a selection to be made, and then ask the server (thats the AJAX part) for a list of the courses in that program, and update the second list accordingly.
However, for your situation I would say that is overkill; it would be far easier to do as jammon's answer says and put the admin for the Grade model within the corresponding Course admin (this is known as an inlined model admin; see the Django documentation on the subject). Unfortunately the example code given in their answer doesn't work, so I've created the following example which works for me.
The basic way of configuring the Django admin is to simply register your models with the admin site in admin.py. You're probably doing something along the following lines:
from django.contrib import admin
from myapp.models import Program, Course, Grade
admin.site.register(Program)
admin.site.register(Course)
admin.site.register(Grade)
Instead, we want to edit the grades for a course within the admin section of the course itself. First, we need to define an inline administration class for the grade:
class GradeInline(admin.TabularInline):
model = Grade
extra = 1
Note you can choose to inherit from either admin.TabularInline or admin.StackedInline. Normally, I prefer the look of the TabularInline but it can depend on your model so try both. The extra option defines how many blank forms will be shown to the user.
Next we need to tell the Course admin page to add the inline forms:
class CourseAdmin(admin.ModelAdmin):
model = Course
inlines = (GradeInline,)
Finally, we no longer want to show the independent grade admin, so we don't need to register it with the admin site. Instead, we must specify that our custom admin class be used for the Course model. This means the final admin.py file for the application is as follows:
from django.contrib import admin
from myapp.models import Program, Course, Grade
class GradeInline(admin.TabularInline):
model = Grade
extra = 1
class CourseAdmin(admin.ModelAdmin):
model = Course
inlines = (GradeInline,)
admin.site.register(Program)
admin.site.register(Course, CourseAdmin)
I had a similar problem, here is the solution I came to:
Define a InlineModelAdmin for Grade in the ModelAdmin of Course.
class CourseAdmin(admin.ModelAdmin):
class GradeInline(admin.StackedInline):
model = Grade
fk_name = 'course'
extra = 2
and so on
Then you can add and edit the grades in the the change_view of the course they belong to and you don't have to select the course at all.
And for telling the courses of different programs apart in the listings I'd redefine course.__unicode__ to return something like 'grade.name (program.name)'.

Where to you monkey patch the Django user model?

I want to monkey patch the user model from Django.
My code:
from django.db import models
from django.contrib.auth.models import User
User.add_to_class('secret_question', models.CharField(max_length="100"))
User.add_to_class('answer', models.CharField(max_length="100"))
User.add_to_class('DOB', models.DateField())
Where do I place this code so that python manage.py syncdb will create the correct table?
I tried the main directory models.py, I tried an app's directory's models.py (these two didn't produce the correct table), and I tried placing it in the settings.py of the project (error, couldn't run).
Please take a look at Storing additional information about users section of the authentication documentation. It suggests a cleaner way to add additional information to a User object.
If you'd like to store additional information related to your users, Django provides a method to specify a site-specific related model -- termed a "user profile" -- for this purpose.
If you really want to monkey patch user model, there already exists an app for this.
Django-primate
A modular django user.
This Django application monkey patches
django in order to have a custom User
model that plugs into the
django.contrib.auth application.

About 20 models in 1 django app

I have started work on a local app for myself that runs through the browser. Having recently gone through the django tutorial I'm thinking that it might be better to use django rather than just plain python.
There's one problem: I have at least 20 models and each will have many functions. Quite simply it's going to create one huge models file and probably huge views too. How do I split them up?
The models are all related so I can't simply make them into separate apps can I?
This is a pretty common need... I can't imagine wading through a models.py file that's 10,000 lines long :-)
You can split up the models.py file (and views.py too) into a pacakge. In this case, your project tree will look like:
/my_proj
/myapp
/models
__init__.py
person.py
The __init__.py file makes the folder into a package. The only gotcha is to be sure to define an inner Meta class for your models that indicate the app_label for the model, otherwise Django will have trouble building your schema:
class Person(models.Model):
name = models.CharField(max_length=128)
class Meta:
app_label = 'myapp'
Once that's done, import the model in your __init__.py file so that Django and sync db will find it:
from person import Person
This way you can still do from myapp.models import Person
"I have at least 20 models" -- this is probably more than one Django "app" and is more like a Django "project" with several small "apps"
I like to partition things around topics or subject areas that have a few (1 to 5) models. This becomes a Django "app" -- and is the useful unit of reusability.
The overall "project" is a collection of apps that presents the integrated thing built up of separate pieces.
This also helps for project management since each "app" can become a sprint with a release at th end.
The models are all related so I cant's
simply make them into separate apps
can I?
You can separate them into separate apps. To use a model in one app from another app you just import it in the same way you would import django.contrib apps.
Having 20 models in one app might be a sign that you should break it up in smaller ones.
The purpose of a Django app is to have a small single-purpose piece of code, that fits nicelly together.
So, if you had a e-commerce site, you might have a shopping_cart app, a billing app, and so on.
Keep in mind that there is really no problem in apps depending on each other (although it's always better if they can be decoupled), but you should not have an app doing two very distinct things.
The article Django tips: laying out an application might help you. As always, take everything you read with a grain of salt (including this answer).
You can break up the models over multiple files. This goes for views as well.
You can split them into separate files and simply have imports at the top of your main models.py field.
Whether you'd really want to is another question.

Categories