How do you use ManyToManyFields between apps without producing circular imports? - python

I have two apps, users and posts, with the models CustomUser and Block in users, and a model Post in posts. I'd like to created a "bookmarked" ManyToMany field for the User, so that they can bookmark any posts they want. It would look something like:
class CustomUser(AbstractUser):
...
neighbors = models.ManyToManyField("CustomUser", blank=True)
blocks = models.ManyToManyField("Block", blank=True)
bookmarked = models.ManyToManyField("Post", blank=True)
...
As you can see, I have quite a few ManyToMany fields already, but they were all for models from the same app users. As for my Post class:
class Post(models.Model):
...
author = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
block = models.ForeignKey(Block, null=True, on_delete=models.SET_NULL)
...
I already imported two models from the users app, CustomUser and Block, into the posts app. I understand that by importing Post into users' models.py, it creates a circular import, at least, it gives me the following error:
ImportError: cannot import name 'CustomUser' from partially initialized module 'users.models' (most likely due to a circular import)
Is there a way to prevent this? I know an option is to just create the bookmarked attribute in the Post model instead of the User model, but I'm reluctant to do so simply because it's a little weird to me personally. Do you know of any other options, or will I have to create the bookmarked attribute in Post as opposed to User?

You can connect the FK relation by using a string as,
author = models.ForeignKey('app_name.CustomUser', on_delete=models.CASCADE)
so that you don't have to import the related model

Related

How to connect models from different apps in Django?

I have a few apps within my Django project. There are two apps that I am currently working with "Application" and "User" and I have two questions related to models:
Question 1:
I want to design it in such a way so that external users submit their contact form on Application/templates/Application/Apply.html and the info would get added to the database. Internal users would be able to add external users as well but from a different template: User/templates/User/AddNewContact.html
I am able to add a new contact from an internal user's perspective:
User/models.py
class Contact(models.Model):
ContactName = models.CharField(max_length = 250, default='')
ContactResidence = models.CharField(max_length = 250, default='')
Tel = models.CharField(max_length = 250, default='')
def get_absolute_url(self):
return reverse('User:ContactDetails', kwargs={'pk': self.pk}
)
def __str__(self):
return self.ContactName
class Locations(models.Model):
contact = models.ForeignKey(Contact, on_delete=models.CASCADE)
Country = models.CharField(max_length=250, default='')
def __str__(self):
return self.Country
I was going to just copy this model and paste it into Application/models.py but there are two problems:
1) I don't want external users to be directed to URL: User:ContactDetails and technically, it is not going to work out because I will build the authentication later on.
2) I feel that by copying and pasting I am breaking the 'don't repeat yourself" rule.
Should I connect two models using the foreign keys? What are the best practices in this case?
Question 2
Am I working with one-to-many relationship according to the model provided? I want to have one contact with his personal info (tel/email/address) and a number of branch locations across the world associated with that contact.
To be used a relationship one to many, you can be doing as after:
On models of father app (father table):
class Department(models.Model):
dept_id = models.AutoField(primary_key=True)
On models of child app (child table):
from appname.models import Department
class Office(models.Model):
office_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=200)
dept = models.ForeignKey(Department, on_delete=models.CASCADE)
It helped me.
Question 1: Well, you don't need to copy paste the model. You can use models from other django apps anytime, just need to import it. Basically what you should do is, instead of linking the url directly to the template in the Applications app, you should connect it to a view. In the view file you can import the models from User.models import *, and use them normally.
Question 2: As far as I understand the question your structure provides what you want: one contact (with personal info) associated with several countries. Except that you should replace Agent by Contact in contact = models.ForeignKey(Agent, on_delete=models.CASCADE)
Question 1: Note that the 'get_absolute_url' method is only called if you don't provide a success url in your view. If you are using a CreateView or FormView you can specify the success url by overriding the get_success_url method, for example:
class ContactCreateView(CreateView):
model = Contact
fields = ['ContactName', 'ContactResidence', 'Tel']
def get_success_url(self):
if not self.request.user.internal: # e.g. internal is a User bool field
return HttpResponseRedirect('some/external/url/')
return super().get_success_url() # call get_absolute_url model method.
The DRY principle is respected.
Question 2: Yes, the question you need to ask yourself is 'does a model instance (In this case Contact) have many instances of another model (Location)?' If the answer is yes, then the M2M field should go into your Contact model. See the django docs explaining the pizza/toppings example.
The apps should be in the same project and you can import one model as:
import appName.models or
from appName.models import ClassName
In app2 models you can use foreignKey or manyTomany after importing the class:
from appsName.models import ClassName
class Person(models.Model):
con = ForeignKey(ClassName)

Correct way to extend AbstractUser in Django?

I'm trying to integrate two django apps where each had their individual auths working. To do that, I'm trying to subclass AbstractUser instead of User. I'm following the PyBB docs and Django#substituting_custom_model. I've removed all migration files in all my apps apart from their individual init.py (including the migrations from the PyBB library sitting in my site-packages). I've also changed the Mysql database to a blank one to start afresh and I'm trying to subclass AbstractUser as shown below.
My Models.py:
from django.contrib.auth.models import User
from django.contrib.auth.models import AbstractUser
from django.db import models
class Student_User(models.Model):
"""
Table to store accounts
"""
su_student = models.OneToOneField(AbstractUser)
USERNAME_FIELD = 'su_student'
su_type = models.PositiveSmallIntegerField(db_column='su_type', default=0)
su_access = models.TextField(db_column='su_access', default='')
su_packs = models.TextField(db_column='su_packs', default='')
REQUIRED_FIELDS = []
def __unicode__(self):
return str(self.su_student)
My settings.py:
AUTH_USER_MODEL = "app.Student_User"
PYBB_PROFILE_RELATED_NAME = 'pybb_profile'
When running makemigrations for my primary app, I get this error:
app.Student_User.su_student: (fields.E300) Field defines a relation with model 'AbstractUser', which is either not installed, or is abstract.
How do I achieve what I am trying to do here?
PS: The app was working fine with onetoone with User without username_field or required_field.
PPS: I just checked the AbstractUser model in my contrib.auth.models and it has class Meta: abstract = True. Ok so its abstract, still, how do I resolve this? I just need one login, currently, two parts of my site, although connected through urls, ask for seperate logins and dont detect each others logins. What do I need to do for this?
You can't have a one-to-one relationship with an abstract model; by definition, an abstract model is never actually instantiated.
AbstractUser is supposed to be inherited. Your structure should be:
class Student_User(AbstractUser):
...

Model not defined

I defined a Question model with a description and a foreign key.
class Question(models.Model):
user = models.ForeignKey(
User,
verbose_name="User",
default=None
)
description = models.CharField(
max_length=60,
#verbose_name=_("Description"),
)
After that, I ran the migrations.
Then, in views.py I created a method which accesses objects of this model:
def own_questions(request):
questions = Question.objects()
return JsonResponse(questions)
The problem is that when I access the URL /questions corresponding to this method, I get:
NameError at /questions/
global name 'Question' is not defined
Why is this happening?
Probably because you haven't imported
from .models import Question
into your views.py
You need to import Questions in your views.py:
from app.models import Question
Also, questions = Question.objects only give you the queryset manager and you can't call that, instead for all questions, you need:
questions = Question.objects.all()
Edit:
I shouldn't assume what you are trying to query from model Question, so here's django doc about how to write ORM for queries.

Django: Fields not found?

I keep getting an error
ImproperlyConfigured at /messages/compose/
Creating a ModelForm without either the 'fields' attribute or the 'exclude' attribute is prohibited; form ComposeForm needs updating.
I totally understand what the error is pointing out however I don't understand why it's showing up in the first place?
Here is the form django wants me to update:
from django import forms
from .models import DirectMessage
class ComposeForm(forms.ModelForm):
class Meta:
model = DirectMessage
and here is my model (complete with fields):
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
user_obj = User.objects.get(username = 'jess')
class DirectMessage(models.Model):
subject = models.CharField(max_length =150)
body = models.CharField(max_length =3000)
sender = models.ForeignKey(User, related_name='sent_direct_messages', null=True, blank=True)
receiver = models.ForeignKey(User, related_name='recieved_direct_messages', null=True, blank=True)
sent = models.DateTimeField(auto_now_add=False, auto_now=False, null=True, blank=True)
read = models.DateTimeField(auto_now_add=False, auto_now=False, null=True, blank=True)
def __unicode__(self):
return self.subject
perhaps there is a problem with my syntax or I'm missing a glaring fundamental error. Any help would be appreciated, let me know if you need any more information/context. Thank you!
I totally understand what the error is pointing out however I don't
understand why it's showing up in the first place?
Err... Because your ModelForm doesn't explicitely specifies either a fields list or an exclude list ?
Here is the form django wants me to update:
class DirectMessageAdmin(admin.ModelAdmin):
This is not a ModelForm, this is a ModelAdmin. Given the url in your error message I don't think it has anything to do with your admin...
As it is described in Django documentation: Creating forms from models: Selecting the fields to use:
It is strongly recommended that you explicitly set all fields that should be edited in the form using the fields attribute. Failure to do so can easily lead to security problems when a form unexpectedly allows a user to set certain fields, especially when new fields are added to a model. Depending on how the form is rendered, the problem may not even be visible on the web page.
The alternative approach would be to include all fields automatically, or blacklist only some. This fundamental approach is known to be much less secure and has led to serious exploits on major websites (e.g. GitHub).
Therefore, you should explicitly include a fields variable in your Meta, regardless of the fields defined in the model. This has to be a tuple of all fields of the model which you need to have in the ModelForm. You can alternatively specify the value as '__all__'.
This has been introduced in Django 1.6:
Changed in Django 1.6:
Before version 1.6, the 'all' shortcut did not exist, but omitting the fields attribute had the same effect. Omitting both fields and exclude is now deprecated, but will continue to work as before until version 1.8.

Django Model Design: Registered and Unregistered Users referenced together

Experimenting with django, I am trying to design a site that will reference registered users of the site as well as non-registered users. Trying to figure out how to best design my models for this, I'd like to give the non-registered user the ability to register and have it linked to the information I already have. I've read some other SO questions that are sort of related so I know I should be using a seperate class (registered & non-registered), but how would I reference 2 different models using a one foreign key?
Models.py:
from django.db import models
from django.contrib.auth.models import User
class NonRegisteredPerson(models.Model):
first_name = models.CharField(max_length=20, default='')
last_name = models.CharField(max_length=20, default='')
email = models.EmailField(max_length=20,default='', blank=True)
class Seat(models.Model):
num = models.CharField(max_length=100, blank=True)
# For the Non Registered User
occupant = models.ForeignKey('NonRegisteredPerson')
# For the Registered User - using Built-in User
occupant = models.ForeignKey(User)
How do I get "occupant" to reference NonRegisteredPerson and User?
Don't know if this matters, but the way I would handle if a NonRegisteredUser signs up for the site, is check if their e-mail exists in the NonRegisteredPerson model, if it does then delete them from that model and add to the Built-in User.
I know this must be a common scenario, but I don't know what or where I should be looking up for this. TIA.

Categories

Resources