My app is full of statements like this:
from my_app.model import Customer
While it works, I'll like to have a way to reference the current app without hard coding the app name into the import. Something like:
from keyword_for_current_app.model import Customer
Is there a clean way of achieving this with Django?
This is more of a python question than a Django one as it relates to how imports work. As far as I know, there isn't a way to do it as you describe, unless you change the structure of your files.
However, it isn't impossible to do something using Django's functionality to dynamically import models. You could swap some of this with importlib if you need a more general import rather than just returning the model class:
from django.apps import apps
# option 1
model = apps.get_model('app.Model')
# option 2
def get_model(model_name):
return apps.get_model(f'myapp.{model_name}')
# option 3
def get_model(model_name):
for model in apps.get_models():
if model.__name__ == model_name:
return model
The problem with option 1 for your use case is that you still need to specify the app name. Option 2 is an improvement, but you are hardcoding the app name into the function. Option 3 is the best, but might be problematic if you have models with the same name but in different apps/tables.
In my opinion, you should import each model explicitly at the top of your files. This is the cleanest and most readable way - if someone were to read your code, they would expect models to be imported this way.
I was referring this link : Django: Get model from string?
. And, I found there is a way to do this by using apps.get_model. But,In my scenario, the model can be from other apps. So, I can't actually name the app_name here. Is there any way to do this ?
If you don't care which app the model comes from, you can do it the following way:
from django.apps import apps
def get_model_from_any_app(model_name):
for app_config in apps.get_app_configs():
try:
model = app_config.get_model(model_name)
return model
except LookupError:
pass
model = get_model_from_any_app('SomeModelName')
But in Django models in different apps can have the same name, i.e. your project can have model Post in your blog app and model Post in your news app etc.
So this way you can end up with not the model you expect, if they have duplicate names across apps (i.e. you probably should not do it this way, just think why in the world would you want a semi-random model?).
Docs which explain the code:
https://docs.djangoproject.com/en/2.0/ref/applications/#django.apps.apps.get_app_configs
https://docs.djangoproject.com/en/2.0/ref/applications/#django.apps.AppConfig.get_model
Hello everyone how is it going?
I've just started using Django recently, and I've started getting my head around it; I need to build a website about cars, with two major APPS:
CarsCatalogue;
News Section;
I find the fact that I can manage the news from the admin panel extremely useful. I have created the typical model:
class Post(models.Model):
title = models.CharField(max_length = 140)
date = models.DateTimeField()
body = models.TextField()
def __str__(self):
return self.title
With the urls.py as follows
url(r'^(?P<pk>\d+)$', DetailView.as_view(
model = Post,
template_name="news/post.html"))
url(r'^$', ListView.as_view(
queryset=Post.objects.all().order_by("-date")[:25],
template_name="news/news.html")),
This is great! I can manage the News app Extremely easyly from the admin panel. Now I have a page for each news: news/1; news/2 etc etc;
But when i go down to the CarsCatalogue, and I would really need to simplify my life because I have plenty of cars with a personal page each to add, I am instead finding myself needing to modify the urls.py for each car I need to add, and it seems I have to modify the views.py for each car -I am using render- am I right?
I mean, does it make sense to have a Views.py with one hundred different functions calling one hundred pages?
And then if I want to create a list with all the urls of the CarCatalogue, having to write every link one by one?
Is this the way to use Django in this case?
I would create another "news-style" APP for CarsCatalogue, that would be so much more easy for me to manage through the Admin Panel, but I need each url to show the car name, like: CarsCatalogue/Seat-Ibiza and not like CarsCatalogue/1.
Maybe I can do something like the news APP, but changing the way urls are generated and shown?
I am asking you all of this after I read the documentation and several Google topics and other resources;
I hope you guys will be able to clear the fog around my head;
With all the Respect such a community deserves,
Sincerely,
-oKi
EDIT n*1
It's been 3 hours of reading, trying, modifying, erasing, trying again.
I read a lot of stuff, but at the same time I got pheraps even more confused, because I found so many things while looking for how to "slug"ify the urls (that is indeed what I was looking for [now I can indeed use the admin panel to do what I wanted!] thanks) that I ended up mixing a lot of stuff. So, using the NEWS application, what I have done so far:
python3.5 manage.py flush, makemigrations, migrate, createsuperuser
I modified the news/models.py, so that it now looks like this:
from django.db import models
from django.template.defaultfilters import slugify
class Post(models.Model):
title = models.CharField(max_length = 140)
date = models.DateTimeField()
body = models.TextField()
slug = models.SlugField(title, max_length=100, unique=True)
def __str__(self):
return self.title
def slug(self):
return slugify(self.title)
I modified the news/admin.py, so that it now looks like this:
from django.contrib import admin
from news.models import Post
admin.site.register(Post)
class NewsAdmin(admin.ModelAdmin):
prepopulated_fields = {"slug": (Post.slug)} <!-- that seems makes sense looking at the Model - I also tryed {"slug": (title,)}, {"slug": (Post.title)}-->
I modified the news/urls.py, so that it now looks like this:
from django.conf.urls import url
from django.views.generic import ListView, DetailView
from news.models import Post
urlpatterns = [
url(r'^$', ListView.as_view(
queryset=Post.objects.all().order_by("-date")[:25],
template_name="news/news.html")),
url(r'^(?P<slug>[\w-]+)$', DetailView.as_view(
model = Post,
template_name="news/post.html")),
]
then I give:
python3.5 manage.py runserver
and it runs with no problem.
So I go to /admin, I create a new post, it creates it...
But then what happens?
It still uses the old "paradigm" to generate the url, including the old keys(id's) it was using before I erased the client... so... what I expected to be something like mysite/news/new-human-level-urld-news
turns out to be mysite/news/11.
LOL :D and, luckily, the browser gives me also an error...
"FieldError at /news/11
Cannot resolve keyword 'slug' into field. Choices are: body, date, id, title"
at the moment I can't find an answer, I find so much stuff that I just don't know how to mix things up. I will keep searching. Help is appreciated! Thanks for now!!! :D
According to my understanding to your problem, what you need is not to create a view function for every car you have.
Indeed, you need to create 1 template (html page) that describes your CarsCatalogue, where all the CarsCatalogues have the same structure with different information. Then the user chooses one CarsCatalogue, let's say from a dropdown control and then you load the data from your database and show your results in the template.
In order to achieve this, have a look on the following topics:
Django forms. And in order to get your data from the database, you need the the id for example to be passed in the url, you can see this answer how to pass an id in django url or from the official website URL Dispatcher.
In few words, django is a simple way to do your website and definitely no need to rewrite your code several times.
NO THERE IS NO NEED TO CREATE MULTIPLE URLS.
From what I understood, you want a catalogue for each car.
Just create a view from where you display the names of all your cars.
Then when you click on a car, make a get request to another view function in the get request pass your selected car details. Now comes the use on Django Templates. Create a Django template for your catalogue, pass the car specific data to it.
You need to read about Django Templates , I think Django templates is something that will solve your problem.
I think the thing you're missing is that the parameter in the URL can be anything, not just a numeric URL. If it's a string, we call that a slug, and the Django admin will automatically create a slug from the fields in your model if you set the prepopulated_fields option.
Then, you can use that slug in the URL:
url(r'^(?P<slug>[\w-]+)$', DetailView.as_view(
model = Car,
template_name="news/car.html"))
and Django will use that field instead of the ID to find the right content to display.
I found a way to fix my problem.
Even though I started off by asking about the CARS Catalogue, I will explain what I did using the NEWS APP instead, as the logic is the same and as that is the APP I have modified in the first EDIT of my post.
Inside the Templates, have a "news.html" page to render the list of all the Post, and a "post.html" to render every single Post.
The following is part of the procedure on how to set an APP that you can manage directly from the ADMIN PANEL, and that uses the SLUG field as URL.
models.py:
from django.db import models
class Post(models.Model):
title = models.CharField(max_length = 140)
body = models.TextField()
slug = models.SlugField(unique=True)
def __str__(self):
return self.title
admin.py:
from django.contrib import admin
from news.models import Post
class PostAdmin(admin.ModelAdmin):
model = Post
prepopulated_fields = {'slug': ('title',)}
admin.site.register(Post, PostAdmin)
urls.py:
from django.conf.urls import url
from django.views.generic import ListView, DetailView
from news.models import Post
urlpatterns = [
url(r'^$', ListView.as_view(
queryset=Post.objects.all(),
template_name="news/news.html")),
url(r'^(?P<slug>[\w-]+)$', DetailView.as_view(
queryset=Post.objects.all(),
model = Post,
template_name="news/post.html")),
]
I hope that can be useful to somebody.
Jeez you gotta study hard to use this Framework :D
Thanks everybody for the support, c ya :)
For Django, the best practice is apparently to have 1 app to do only 1 thing and to do 1 thing right...and so they can be "plugged and played" to other projects
Consider I have a library application and there are 3 apps. book, member and loan. Each of them has their own model.
So in, say forms.py in loan, I will need to do something like this:
from book.models import Book
from member.models import Model
from .models import Loan
#Other key imports
class CreateLoanForm(forms.Form):
# Some fields
def save(self):
# Retrieve fields
selected_book = self.cleaned_data.get('selected_book')
selected_member = self.cleaned_data.get('selected_member')
new_loan = Loan(
book=Book.objects.get(id=selected_book),
member=Member.objects.get(id=selected_member),
date_start=self.cleaned_data.get('date_start'),
date_end=self.cleaned_data.get('date_end')
)
My questions are:
Doesn't the above from book.models ... violate the ability to plug and play apps?
Is there a better way to import these models? Am I violating best practices when doing so?
For "one app to do one thing, and one thing right" does not mean that it has to have only one model. If you are writing a Library Application then it should have the book, member, and loan models in the same app.
In the context of you question, however, one possible practice to use models from application A in application B would be to define the model as a pluggable setting in app B. See for example what the auth module is doing here: https://docs.djangoproject.com/en/1.9/topics/auth/customizing/#substituting-a-custom-user-model
I'd like to retrieve automatically, in a loop, the names of the models located in a specific Django app inside my project. Does someone know how to do that ?
Best Regards
Guillaume
from django.db import get_models, get_app
app = get_app('myappname')
models = get_models(app)