Django 'Sites' Model - what is and why is 'SITE_ID = 1'? - django

I am trying to work with Sites Model of Django.
I dont quite understand why SITE_ID should be SITE_ID = 1.
in the docs:
The ID, as an integer, of the current site in the django_site database
table. This is used so that application data can hook into specific
sites and a single database can manage content for multiple sites.
why 1? what is the current site? this is not clearly explained in the docs.
lets say, I have www.coolsite.com and some other subdomains like www.wow.coolsite.com and www.awesome.coolsite.com
I want to render different content depending on domain name.
my question is, or better, are:
Do I have to add all those domains into Sites Table in DB?
if so, how should I set SITE_ID in settings? Do I have to set all ids like SITE_ID = 1, SITE_ID = 2.. etc?
what does current site has to do with SITE_ID = 1?
I am a bit confused here.
I thought, each Site (e.g. www.wow.coolsite.com) should be a separate django project so that they can have their own settings.py? and in each of those settings.py's, I will set the id of that page from Sites table? but then there are many django projects which also doesnot make sense to me.

Django was created from a set of scripts developed at a newspaper to publish content on multiple domains; using one single content base.
This is where the "sites" module comes in. Its purpose is to mark content to be displayed for different domains.
In previous versions of django, the startproject script automatically added the django.contrib.sites application to INSTALLED_APPS, and when you did syncdb, a default site with the URL example.com was added to your database, and since this was the first site, its ID was 1 and that's where the setting comes from.
Keep in mind that starting from 1.6, this framework is not enabled by default. So if you need it, you must enable it
The SITE_ID setting sets the default site for your project. So, if you don't specify a site, this is the one it will use.
So to configure your application for different domains:
Enable the sites framework
Change the default site from example.com to whatever your default domain is. You can do this from the django shell, or from the admin.
Add your other sites for which you want to publish content to the sites application. Again, you can do this from the django shell just like any other application or from the admin.
Add a foreign key to the Site model in your object site = models.ForeignKey(Site)
Add the site manager on_site = CurrentSiteManager()
Now, when you want to filter content for the default site, or a particular site:
foo = MyObj.on_site.all() # Filters site to whatever is `SITE_ID`
foo = MyObj.objects.all() # Get all objects, irrespective of what site
# they belong to
The documentation has a full set of examples.

Things would be much easier to understand if Django's default SiteAdmin included the id field in the list_display fields.
To do this, you can redefine SiteAdmin (anywhere in your app, but I'd recommend your admin.py or maybe your urls.py) like this:
from django.contrib import admin
from django.contrib.sites.models import Site
admin.site.unregister(Site)
class SiteAdmin(admin.ModelAdmin):
fields = ('id', 'name', 'domain')
readonly_fields = ('id',)
list_display = ('id', 'name', 'domain')
list_display_links = ('name',)
search_fields = ('name', 'domain')
admin.site.register(Site, SiteAdmin)
After including this code snippet, the ID for each "Site" will be shown in the first column of the admin list and inside the form as a read only field. These 'id' fields are what you need to use as SITE_ID:
The concept is that each different site runs in a different application server instance, launched using a different yourdomain_settings.py that then includes a base_settings.py with the rest of the common configuration.
Each of these yourdomain_settings.py will define its own SITE_ID and all other different settings.py parameters that they need to look and be different from each other (static resources, templates, etc.) then you'll define a DJANGO_SETTINGS_MODULE environment variable pointing to that specific yourdomain_settings.py file when launching the application server instance for that domain.
A further note: get_current_site(request) does need request to be available for it to work. If your code doesn't have one, you can use Site.objects.get_current() that however will need a SITE_ID properly defined in the running application server's settings.

This is a late answer but for anyone else having SITE_ID issues and Site problems.
Inside the database, django has a django_site table with(id, domain, name). This is where django stores the SITE_IDs. Mine was actually 5 in the database but i had it set to SITE_ID=1 in the settings.
Knowing that, i can now go back to the database and clear it to get back to zero or use the actual id in the database.

This is covered in the documentation for the Sites framework:
In order to serve different sites in production, you’d create a
separate settings file with each SITE_ID (perhaps importing from a
common settings file to avoid duplicating shared settings) and then
specify the appropriate DJANGO_SETTINGS_MODULE for each site.
But if you didn't want to do it that way, you can not set SITE_ID at all and just look up the current site based on the domain name in your views using get_current_site:
from django.contrib.sites.shortcuts import get_current_site
def my_view(request):
current_site = get_current_site(request)
if current_site.domain == 'foo.com':
# Do something
pass
else:
# Do something else.
pass

This link explains it:
You’ll want to create separate settings files for each domain you’re adding; each one will need its own MEDIA_URL and other settings. You’ll also want to do two things to make sure everything works out properly for administering the different sites:
Create a new Site object in your admin for each domain, and put the id of that Site into its settings file as SITE_ID so Django knows which site in the database corresponds to this settings file.
In the settings file for your original site (the one with id 1), add the other sites’ settings files to the ADMIN_FOR setting, to let Django know that this one instance of the admin application will handle all of the sites.
Also, if you wanna figure out how to modify models and set the views You may take a look at this link:
https://django.cowhite.com/blog/managing-multiple-websites-with-a-common-database-in-django-the-sites-framework/

Related

Django. One project. Separate auth systems and templates for different apps

I have a project with the following structure:
main_app
accounts_app (stores CustomUser model)
my_app1
users_app (inside my_app1)
my_app2
users_app (inside my_app2)
The reason behind separate users_app is that I want to reuse my_app1 and my_app2 in other projects. So I want them to be as separate as possible.
I want to have a separate auth system (custom user model, registration, templates, login/logout redirect, etc) for each app. So for example, let's say I have my-app1 at url localhost:8000/my-app1/. This means that registration will be at /my-app1/signup/, login at /my-app1/login/ and login redirect goes to /my-app1/
I also want to use allauth on some or all apps.
Questions:
Does this project structure even make sense? (yes/no)
Is it possible to have separate signup/sigin/etc templates for each app using allauth? (yes/no - link to doc?) I couldn't find the answer to this.
How do I avoid DB conflicts with migrations when creating custom user for each app? Right now I'm inheriting from AbstractUser in each app's model.py, like so: class MainCustomUser(AbstractUser), class AppOneUser(AbstractUser), class AppTwoUser(AbstractUser) But that doesn't seem to work.
Answers to these questions will serve me as guidance in the right direction, right now I sort of confused myself a bit.
settings.py:
...
# django-allauth config
SITE_ID = 1
LOGIN_REDIRECT_URL = 'main_app:index'
ACCOUNT_LOGOUT_REDIRECT = 'main_app:index'
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'allauth.account.auth_backends.AuthenticationBackend',
)
...

How to make raw text file (to edit robots.txt) editor in Django Wagtail admin

The goal is to create a text file editor within the Wagtail admin site.
Specifically for robots.txt file. I want the admin to update the file from the admin.
How can I do that?
There are some considerations to make for the robots.txt file hitting your server and doing a database read each time. You probably will want to consider some kind of caching layer here, however I have answered assuming you will resolve that and just want to get a Wagtail 'editor' solution here.
Wagtail provides a contrib.settings module that allows you to have a settings model per site. This is a very helpful module that allows users, usually admin only users, to modify content that is specific to a site but does not suit the idea of a 'page'.
https://docs.wagtail.org/en/stable/reference/contrib/settings.html
Settings model
In your app's models.py file - set up a new settings model.
from django.db import models
from wagtail.admin.panels import FieldPanel
from wagtail.contrib.settings.models import BaseSetting, register_setting
#register_setting
class MySettings(BaseSetting):
robots = models.TextField(blank=True)
panels = [
FieldPanel('robots'),
]
robots.txt view & template
There are lots of ways to load a View with robots.txt
Accordbox has a good tutorial on how to set up a robots.txt view https://www.accordbox.com/blog/wagtail-seo-guide/#robotstxt
Once you have your view, you can get the settings output via the model class.
# using in your view
def robots_view(request):
robots = MySettings.for_request(request).robots
## return the content in your view
Alternatively you can use in your template context via an injected template context variable
note: 'wagtail.contrib.settings.context_processors.settings' must be added to context_processors
User-agent: *
{{ settings.app_label.MySettings.robots }}
Considerations
If possible, it would always be better to serve a static file here, or strongly cache this value at a minimum.
Validation is a must - make it hard for your editors to break your website, remember that search engines only crawl this semi-regularly and breaking this could mean a few days of de-listing or broken listings if your editors are not careful.
Permissions are a must, maybe even a two step process to push these changes live somehow?
You could possibly put a robots field on your HomePage model and access the data in your robots view. This kind of breaks the concept of this model only reflecting the home page but then extending to the 'root' content.

Get the site_id of the user_id in Django?

I'm really struggled by this. I looked through the Django documentation and I couldn't find a method to get the site_id of the currently logged in user_id with Sites framework.
I want to build a blogpost form and I wan't to include the site_id so that the content that the user publish will go to that particular site_id.
The user will only have permissions to publish to one site_id.
To clarify:
Publish content to the site_id that the user_id belongs to.
Is there a way to do this?
A user does not belong to a site. A request does.
If, somehow, in your Django project sites are owned by users, then you should add a new field to your User model. Something like this:
class MyUserModel(...):
...
blog = models.OneToOneField(Site)

what is the use of SITE_ID settings in django?

When I am doing the flatpage tutorial, I was getting error for SITE_ID not being set. I inserted SITE_ID=1 in the settings file and everything worked fine. But I don't know what this actually means.
I read through the django docs. but I am not totally clear what its use.
when will I use something like SITE_ID=2.
On the same note, I used the following snippet in my code without actually knowing what it does:
current_site=Site.objects.get_current()
I assume this has something to do with SITE_ID but may be not.
Some example to demonstrate where SITE_ID could take different values than 1 would help.
It is helpful is you use your code on multiple sites, or if you share a database with another site. An example from the documentation:
from django.db import models
from django.contrib.sites.models import Site
from django.contrib.sites.managers import CurrentSiteManager
class Photo(models.Model):
photo = models.FileField(upload_to='/home/photos')
photographer_name = models.CharField(max_length=100)
site = models.ForeignKey(Site)
objects = models.Manager()
on_site = CurrentSiteManager()
With this model, Photo.objects.all() will return all Photo objects in the database, but Photo.on_site.all() will return only the Photo objects associated with the current site, according to the SITE_ID setting.
Put another way, these two statements are equivalent:
Photo.objects.filter(site=settings.SITE_ID)
Photo.on_site.all()
From documentation:
SITE_ID
Default: Not defined
The ID, as an integer, of the current site in the django_site database table. This is used so that application data can hook into specific sites and a single database can manage content for multiple sites.

Where do I set the domain for my Django Sites framework site, when I only have one?

I have a Django project for a simple blog/forum website I’m building.
I’m using the syndication feed framework, which seems to generate the URLs for items in the feed using the domain of the current site from the Sites framework.
I was previously unaware of the Sites framework. My project isn’t going to be used for multiple sites, just one.
What I want to do is set the domain property of the current site. Where in my Django project should I do that? Somewhere in /settings.py?
If I understand correctly, Sites framework data is stored in the database, so if I want to store this permanently, I guess it’s appropriate in an initial_data fixture.
I fired up the Django shell, and did the following:
>>> from django.contrib.sites.models import Site
>>> one = Site.objects.all()[0]
>>> one.domain = 'myveryspecialdomain.com'
>>> one.name = 'My Special Site Name'
>>> one.save()
I then grabbed just this data at the command line:
python manage.py dumpdata sites
And pasted it into my pre-existing initial_data fixture.
The other answers suggest to manually update the site in the admin, shell, or your DB. That's a bad idea—it should be automatic.
You can create a migration that'll do this automatically when you run your migrations, so you can be assured it's always applied (such as when you deploy to production). This is also recommended in the documentation, but it doesn't list instructions.
First, run ./manage.py makemigrations --empty --name UPDATE_SITE_NAME myapp to create an empty migration. Then add the following code:
from django.db import migrations
from django.conf import settings
def update_site_name(apps, schema_editor):
SiteModel = apps.get_model('sites', 'Site')
domain = 'mydomain.com'
SiteModel.objects.update_or_create(
pk=settings.SITE_ID,
defaults={'domain': domain,
'name': domain}
)
class Migration(migrations.Migration):
dependencies = [
# Make sure the dependency that was here by default is also included here
('sites', '0002_alter_domain_unique'), # Required to reference `sites` in `apps.get_model()`
]
operations = [
migrations.RunPython(update_site_name),
]
Make sure you've set SITE_ID in your settings. Then run ./manage.py migrate to apply the changes :)
You can change it using django admin site.
Just go to 127.0.0.1:8000/admin/sites/
For those who are struggling to find "Sites" section on Django's admin page, for newer Django versions you need to enable the optional Sites Framework like so:
On your settings.py file, add this to your "INSTALLED_APPS":
'django.contrib.sites'
Then specify an ID for the default site (since it's probably your first site to be specified, you can use ID 1):
SITE_ID = 1
Run your migrations and check if the "Sites" section is available on your Django's admin page.
More details at https://docs.djangoproject.com/en/3.0/ref/contrib/sites/#enabling-the-sites-framework
You can modify the Site entry in your database manually. Navigate to the table called 'django_site'. Then, you should only see one entry (row). You'll want to modify the field (column) named 'domain'.