Как изменить модели, которые уже перенесены в базу данных? - PullRequest
2 голосов
/ 12 февраля 2020

Я новичок в django, я хочу изменить мои модели (которые уже есть в моей базе данных sqlite3) без ошибок всего моего проекта. В прошлый раз, когда я модифицировал свои модели, мне потребовались часы, чтобы исправить это, потому что я прослушал весь проект. Так что я не хочу делать ту же ошибку, не могли бы вы, ребята, помочь мне с командами, пожалуйста? Спасибо за помощь

models.py (на данный момент)

from django.db import models

THE_GENDER = [
    ("Monsieur", "Monsieur"),
    ("Madame", "Madame")
]


class Post(models.Model):


    name = models.CharField(max_length=100)
    email = models.CharField(max_length=100)
    gender = models.CharField(max_length=8, choices=THE_GENDER)
    number = models.CharField(max_length=100)


    def __str__(self):
        return self.name

Правка, которую я хочу внести в модели (я хочу добавить автора модели в модели. Поэтому я знать, кто делает форму.)

from django.db import models
from django.contrib.auth.models import User

THE_GENDER = [
    ("Monsieur", "Monsieur"),
    ("Madame", "Madame")
]


class Post(models.Model):


    name = models.CharField(max_length=100)
    email = models.CharField(max_length=100)
    gender = models.CharField(max_length=8, choices=THE_GENDER)
    number = models.CharField(max_length=100)
    author = models.ForeignKey(User, on_delete=models.CASCADE)


    def __str__(self):
        return self.name

мои файлы admin.py (не уверен, что если мне нужно оставить то же самое или изменить его)

from django.contrib import admin
from .models import Post

admin.site.register(Post)

мои файлы form.py

from django import forms
from .models import Post
from crispy_forms.helper import FormHelper


class post_form(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        super(post_form, self).__init__(*args, **kwargs)

        self.helper = FormHelper(self)

    class Meta:
        model = Post
        fields = ["name", "email", "gender", "number"]


views.py

from django.shortcuts import render
from django.http import HttpResponse
from .forms import post_form


# Create your views here.
def home(request):
    form = post_form(request.POST or None)
    if form.is_valid():
        form.save()

    context = {
        "form": form
    }


    return render(request, "sms/home.html", context)

Ответы [ 4 ]

4 голосов
/ 12 февраля 2020

Чтобы уточнить мой комментарий выше ...

Добавление нового ненулевого значения ForeignKey в Django обычно состоит из трех этапов.

  1. Сначала добавьте новый ForeignKey для определения вашей модели с null=True и запустите makemigrations. Это создаст миграцию, которая добавит поле, ничего особенного в этом нет. Выполнение этой миграции добавит столбец со всеми строками, имеющими NULL в качестве значения.
  2. Во-вторых, вы создаете новую пустую миграцию для того же приложения (makemigrations --empty), затем редактируете эту миграцию, чтобы она содержала миграция данных шаг. Здесь вам нужно, в соответствии с вашей бизнес-логикой c, выбрать какое-либо значение для нового внешнего ключа.
  3. В-третьих, вы измените ForeignKey в определении модели, установив null=False, и создайте третья миграция с makemigrations. Django спросит, имели ли вы какое-то отношение к null s - вам нужно сказать, что "да, я клянусь, что имею" (так как вы это сделали, выше в шаге 2).

На практике, для упрощенной версии вопроса ОП, где мы хотим добавить внешний ключ пользователя:

Исходное состояние

class Post(models.Model):
    name = models.CharField(max_length=100)

1a. Добавьте пустое поле.

class Post(models.Model):
    name = models.CharField(max_length=100)
    author = models.ForeignKey(User, null=True, on_delete=models.CASCADE))

1b. Запустите makemigrations.

$ python manage.py makemigrations
Migrations for 'something':
  something/migrations/0002_post_author.py
    - Add field author to post

2a. Создайте новую пустую миграцию.

$ python manage.py makemigrations something --empty -n assign_author
Migrations for 'something':
  something/migrations/0003_assign_author.py

2b. Отредактируйте файл миграции.

Более подробную информацию о миграции данных можно найти, как всегда, в руководстве .

from django.db import migrations


def assign_author(apps, schema_editor):
    User = apps.get_model('auth', 'User')  # or whatever is your User model
    Post = apps.get_model('something', 'Post')  # or wherever your Post model is
    user = User.objects.filter(is_superuser=True).first()  # Choose some user...
    assert user  # ... and ensure it exists...
    Post.objects.all().update(author=user)  # and bulk update all posts.


class Migration(migrations.Migration):

    dependencies = [...]

    operations = [
        migrations.RunPython(assign_author, migrations.RunPython.noop),
    ]

3a. Сделайте поле необнуляемым.

class Post(models.Model):
    name = models.CharField(max_length=100)
    author = models.ForeignKey(User, null=False, on_delete=models.CASCADE))

3b. Запустите Makemigrations.

Правдиво ответьте на вопрос - вы только что добавили операцию Run Python.

$ python manage.py makemigrations something -n post_author_non_null
You are trying to change the nullable field 'author' on something. to non-nullable without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
 2) Ignore for now, and let me handle existing rows with NULL myself (e.g. because you added a RunPython or RunSQL operation to handle NULL values in a previous data migration)
 3) Quit, and let me add a default in models.py
Select an option: 2
Migrations for 'something':
  something/migrations/0004_post_author_non_null.py
    - Alter field author on post

Все готово!

Запуск migrate будет Теперь запустите эти три миграции, и ваша модель будет иметь author без потери данных.

1 голос
/ 12 февраля 2020

Сначала вам нужно отредактировать models.py

# models.py
class Post(models.Model):
    name = models.CharField(max_length=100)
    email = models.CharField(max_length=100)
    gender = models.CharField(max_length=8, choices=THE_GENDER)
    number = models.CharField(max_length=100)
    <b>author = models.ForeignKey(User, on_delete=models.CASCADE) # add new field</b>

    def __str__(self):
        return self.name

Поскольку вы добавили новое поле author, вам нужно выполнить makemigrations, а затем migrate.
Во время миграции вас могут попросить указать значение по умолчанию для author поле для существующих Post экземпляров . В это время вы можете «интерактивно» предоставлять значения через Django shell . Любые действительные PK из User будут действительными входными данными в течение времени. К вашему сведению: Это однократный процесс

После этого измените ваш home(...) вид как,

<b>from django.contrib.auth.decorators import login_required


@login_required</b>
def home(request):
    form = post_form(request.POST or None)
    if form.is_valid():
        <b>instance = form.save(commit=False)
        instance.auther = request.user
        instance.save()</b>

    context = {
        "form": form
    }

    return render(request, "sms/home.html", context)

Примечание: Вы должны использовать @login_required(...) для получения аутентифицированного пользователя в request.user атрибут.

0 голосов
/ 12 февраля 2020

Вы можете решить эту проблему по-разному.

Если данных нет, вы можете легко добавить новое поле и запустить миграцию. С вами должно быть все в порядке

Если у вас уже есть посты, вы можете разрешить обнуление поля, а затем выполнить миграцию, прежде чем заполнять его.

Более интуитивный способ сделать это, особенно если у вас уже есть данные, чтобы назначить пользователя на пост во время выполнения миграций.

Сначала создайте миграцию с разрешенным нулевым полем автора, затем

author = models.ForeignKey(User, null=True, on_delete=models.CASCADE)

manage.py makemigrations manage.py migrate

, после этого удалите атрибут null.

author = models.ForeignKey(User, on_delete=models.CASCADE)

manage.py makemigrations

откройте созданный файл миграции, который будет вам измените его с помощью пользовательского метода, как показано ниже, и запустите миграцию после этого.

from django.db import migrations, models


# create custom method
def add_author(apps, schema_editor):
    User = apps.get_model('auth', 'User')  
    Post = apps.get_model('something', 'Post')  
    posts = Post.objects.all()
    author = User.object.get(id=1) # whichever user you want to use as the author
    for post in posts:
        post.author = author
        post.save()

class Migration(migrations.Migration):

    dependencies = [
        ('posts', '00000000'),
    ]

    operations = [
        migrations.RunPython(add_author), # this will run the method above
        migrations.AlterField(
            model_name='post',
            name='author',
            field=models.ForeignKeyField...,
        ),

    ]

проверьте эту ссылку для более подробного объяснения того, как это работает

0 голосов
/ 12 февраля 2020

Ваша миграция сложна, потому что вы хотите, чтобы каждое сообщение было связано с пользователем. Чтобы обойти это, вы можете сделать следующее:

Добавьте новое поле с помощью null=True models.ForeignKey(User, on_delete=models.CASCADE, null=True), затем manage.py makemigrations, затем вам нужно решить, что делать. Либо (1) добавьте авторов к существующим сообщениям вручную, либо (2) удалите их всех

manage.py shell
from x.models import Post
from django.contrib.auth.models import User

# 1
user = User.objects.get(id=1) # get your user
Post.objects.all().update(author=user)

# or 2
Post.objects.all().delete()

, после чего вы можете окончательно настроить авторов так, как вы хотите: models.ForeignKey(User, on_delete=models.CASCADE) снова manage.py makemigrations

См. Комментарии для получения дополнительной информации

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...