<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های شایگان هوشیاری</title>
        <link>https://virgool.io/feed/@sh.hooshyari</link>
        <description>خدا را سرگرم کن</description>
        <language>fa</language>
        <pubDate>2026-06-07 09:20:30</pubDate>
        <image>
            <url>https://static.virgool.io/images/default-avatar.jpg</url>
            <title>شایگان هوشیاری</title>
            <link>https://virgool.io/@sh.hooshyari</link>
        </image>

                    <item>
                <title>دریافت اطلاعات بورس تهران با پایتون</title>
                <link>https://virgool.io/@sh.hooshyari/%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D8%A7%D8%B7%D9%84%D8%A7%D8%B9%D8%A7%D8%AA-%D8%A8%D9%88%D8%B1%D8%B3-%D8%AA%D9%87%D8%B1%D8%A7%D9%86-%D8%A8%D8%A7-%D9%BE%D8%A7%DB%8C%D8%AA%D9%88%D9%86-mgaev4iytip6</link>
                <description>تابلوی سهام بانک دی در سایت بورس تهرانیه مدت مشغول یادگرفتن بازار بورس بودم و استفاده از برنامه نویسی توی اون بودم. از اونجایی که دریافت اطلاعات از بورس تهران با مشکلاتی همراه هست و اکثرا زمانی که تلاش میکردم این اطلاعات رو دانلود کنم یا ناقص بود یا بسیار کند. دنبال راهی ساده برای گرفتن این اطلاعات بودم؛ در نتیجه بعد از اینکه تونستم راه حل رو پیدا کنم اون رو به یه پکیج پایتون تبدیل کردم. این پکیج روی github و pypi هست.هدف این پکیج دادن قابلیت دسترسی به اطلاعات سایت بورس تهران،‌ ذخیره در فایل یا دسترسی به اون در کد پایتون هست. برای مثال دانلود سابقه معاملات یک سهم یا اطلاعات بنیادی سهم با داشتن عنوان نماد بدون نیاز به دانلود اطلاعات کل نمادها.نصب pytse_clientpip install pytse_clientکارهایی که میشه با این پکیج انجام داد: دانلود سابقه معاملات یک یا تمام نمادها و ذخیره اون در فایل یا استفاده در کددریافت اطلاعات فاندامنتال یک نماد شامل(eps, p/e ,حجم مبنا)گرفتن خروجی DataFrame سابقه معاملات نمادروش استفادهدانلود سابقه نماد(ها)برای دانلود سابقه نماد باید از تابع download استفاده کرد این تابع یک یا چند نماد رو میگره و دیتای اون هارو برمیگردونه و قابلیت ذخیره این اطلاعات در قالب csv هم داره:import pytse_client as tse
tickers = tse.download(symbols=&amp;quotولملت&amp;quot)
tickers[&amp;quotولملت&amp;quot]
            date     open     high  ...     volume  count    close
0     2009-02-18   1050.0   1050.0  ...  330851245    800   1050.0
1     2009-02-21   1051.0   1076.0  ...  335334212   6457   1057.0
2     2009-02-22   1065.0   1074.0  ...    8435464    603   1055.0
3     2009-02-23   1066.0   1067.0  ...    8570222    937   1060.0
4     2009-02-25   1061.0   1064.0  ...    7434309    616   1060.0
...          ...      ...      ...  ...        ...    ...      ...
2323  2020-04-14   9322.0   9551.0  ...  105551315  13536   9400.0
2324  2020-04-15   9410.0   9815.0  ...  201457026  11322   9815.0
2325  2020-04-18  10283.0  10283.0  ...  142377245   8929  10283.0
2326  2020-04-19  10797.0  10797.0  ...  292985635  22208  10380.0
2327  2020-04-20  10600.0  11268.0  ...  295590437  16313  11268.0تابع download سابقه سهام رو دانلود میکنه و در نهایت یه دیکشنری برمیگردونه که با استفاده از نماد سهم میشه سابقه اون رو گرفت.این تابع رو میشه برای چند سهم هم استفاده کرد یعنی:tse.download(symbols=[&amp;quotوبملت&amp;quot, &amp;quotولملت&amp;quot])
tickers[&amp;quotولملت&amp;quot]
tickers[&amp;quotوبملت&amp;quot]یا اگر میخواید اطلاعات کل سهام ها دانلود بشه:tse.download(symbols=&amp;quotall&amp;quot)برای اینکه اطلاعاتی که دانلود میشه رو توی فایل نوشته بشه باید به تابع مقدار write_to_csv=True رو داد یعنی:tse.download(symbols=&amp;quotall&amp;quot, write_to_csv=True)این کد بعد از اجرا توی یه فولدر کنار همونجایی که اجرا میشه درست میکنه و سابقه این سهام هارو اونجا ذخیره میکنه.کار با اطلاعات یک سهامبرای گرفتن اطلاعات یک سهام باید از کلاس Ticker استفاده کرد(واسه دیدن همه‌ی اطلاعاتی که از یه سهام میشه دید این ویکی رو چک کنید):import pytse_client as tse

ticker = tse.Ticker(&amp;quotوبملت&amp;quot)
print(ticker.history)  # سابقه قیمت سهم
print(ticker.title)  # نام شرکت
print(ticker.url)  # آدرس صفحه سهم
print(ticker.group_name)  # نام گروه
print(ticker.eps)  # eps
print(ticker.p_e_ratio)  # P/E
print(ticker.group_p_e_ratio)  # group P/E
print(ticker.base_volume)  # حجم مبنا
print(ticker.last_price)  # آخرین معامله
print(ticker.adj_close)  # قیمت پایانیوقتی به Ticker اسم یک سهم داده میشه اطلاعات اون رو از سایت tsetmc.com میگیره و به شکلی که میبینید میشه به اون دسترسی داشت.(اطلاعات حقیقی و حقوقی اضافه شده و توی گیتهاب میتونید نحوه استفاده اون رو ببینید)نکته:تو تجربه‌ای که داشتم سایت tsetmc گاهی اوقات مقدار زیادی طول میکشه یا ارور میده و نمیشه به سابقه سهام چندین بار دسترسی داشت. برای حل این مشکل توی تابع download یه سری راه حل استفاده کردم که سرعت خوبی داشته باشه و سابقه ناقص نباشه. برای اینکه سرعت بیشتر برای دسترسی به اطلاعات یه سهم از طریق Ticker داشته باشید میشه اول سابقه سهم رو با تابع download دانلود کرد بعد وقتی که Ticker رو استفاده کنید به جای دانلود دوباره اطلاعات، اطلاعات از روی فایل گرفته میشه یعنی:tse.download(symbols=&amp;quotوبملت&amp;quot, write_to_csv=True)  # optional
ticker = tse.Ticker(&amp;quotوبملت&amp;quot)فرق این کد با کد بالا این هست که قبل از استفاده از Ticker برای گرفتن اطلاعات سهام وبملت اول تابع download رو صدا کردیم و مقدار write_to_csv رو True گذاشتیم. اینطوری اگر چند جای دیگه هم بخوایم با Ticker به اطلاعات وبملت دسترسی داشته باشیم لازم نیست که اطلاعات دانلود بشه و از روی فایل ذخیره شده خونده میشه. پس اگر لازم دارید که اطلاعات تعداد زیادی سهم رو بررسی کنید به جای گرفتن دونه به دونه با استفاده از Ticker یک بار اطلاعات اون هارو دانلود کنید بعد با استفاده از Ticker اون اطلاعات رو بخونید.لیست تمام نمادهای بورس تهرانبعد از اینکه اطلاعات رو دانلود می‌کنیم و میخوایم با کلاس Ticker با اطلاعات نمادها کار کنیم لازم هست نام نماد رو به Ticker بدیم. مثلا برای اینکه بعد از دانلود سابقه معامله تمام سهم ها، معاملات روز آخر هر سهم رو ببینید:import pytse_client as tse

tse.download(symbols=&amp;quotall&amp;quot)

for symbol in tse.all_symbols():
    ticker = tse.Ticker(symbol)
    df = ticker.history
    print(df.tail(1))مثال استفاده از این پکیج(استراتژی SMA Cross)فرض کنید بخوایم یه سهم رو تحلیل کنیم و ببینیم که آیا طبق استراتژی SMA زمان خرید اون سهام هست یا نه.import pytse_client as tse

ticker = tse.Ticker(&amp;quotوبملت&amp;quot)
history = ticker.history


def sma(series, periods: int, ):
    return series.rolling(window=periods, min_periods=periods).mean()


sma_10 = sma(history.close, 10)
sma_20 = sma(history.close, 20)
buy_signals = (
        (sma_10 &gt; sma_20) &amp;
        (sma_20.shift(1) &gt; sma_10.shift(1))
)
print(buy_signals.tail(60))خروجی این کد یه لیست هست از مقادیر True و False که نشون میده آیا اون روز بر اساس استراتژی تعریف شده این سهم رو خرید یا نه.جمع‌بندیتوی این مطلب کار‌هایی که با این پکیج میشه انجام داد رو نوشتم و این قابلیت‌ها به مرور بیشتر خواهند شد،در حال حاضر توضیحات استفاده  از پکیج توی گیتهاب پروژه خواهد بود.اگر از پکیج استفاده کردید و نظری دربارش داشتید خوشحال میشم بدونم و اگر مشکلی توی اجرا هم پیش اومد میتونید واسش issue درست کنید.</description>
                <category>شایگان هوشیاری</category>
                <author>شایگان هوشیاری</author>
                <pubDate>Tue, 26 May 2020 18:07:18 +0430</pubDate>
            </item>
                    <item>
                <title>بررسی سورس جنگو برای باگ امنیتی CVE-2019-19844</title>
                <link>https://virgool.io/@sh.hooshyari/%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D8%B3%D9%88%D8%B1%D8%B3-%D8%AC%D9%86%DA%AF%D9%88-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%A8%D8%A7%DA%AF-%D8%A7%D9%85%D9%86%DB%8C%D8%AA%DB%8C-cve-2019-19844-mjrtns0h6o88</link>
                <description>مدتی پیش از طرف Github یه نوتیفیکیشن گرفتم درباره اینکه ورژن جنگوی یکی از پروژه هام یک باگ امنیتی داره که توضحاتش توی این صفحه هست. این باگ میتونه مشکلی توی سیستم بازیابی پسورد ایجاد بکنه و به حمله کننده اجازه بده تا پسورد کاربر رو عوض کنه. توی این مطلب درباره این باگ (که بار اولی نبود که دیده می‌شد و توی گیتهاب هم بود) توضیح میدم و سورس جنگو رو هم برای اینکه بهتر درک کنیم این باگ چطور بوجود اومده و راه برطرف کردنش چی بوده نگاه میکنیم.توضیح باگفرض کنیم کاربری با ایمیلی به آدرس ali@domain.example توی سایت هست، حمله کننده برای بازیابی پسوورد از ایمیل alı@domain.example استفاده میکنه؛ و توی کد این دوتا ایمیل با هم مساوی حساب میشن! و در نتیجه ایمیل اشتباهی به ایمیل alı@domain.example ارسال میشه.این باگ توی بخش reset password جنگو اتفاق میوفته، حمله کننده(که ایمیل قربانی رو میدونه) توی فرم reset password ایمیلی رو وارد میکنه که جنگو فکر میکنه ایمیل قربانی هست اما درواقع ایمیلی هست که حمله کننده به اون دسترسی داره و جنگو لینک عوض کردن پسوورد رو به ایمیل اشتباه میفرسته.اما چطور این دوتا ایمیل متفاوت با هم اشتباه گرفته می‌شوند؟ ایمیل ها توی دیتابیس معمولا به یه شکل(حروف کوچک یا بزرگ) ذخیره می‌شوند و وقتی که میخوایم ببینیم یه ایمیل توی دیتابیس هست یا نه راهش اینه که اون ایمیل رو بدون حساسیت به بزرگ و کوچک بودن پیدا کنیم، تا اینجا این کار درست هست اما قسمتی وجود داره که باید بهش توجه بشه. بعضی کاراکتر ها هستند که وقتی به حروف کوچیک تبدیل می‌شوند با هم مساوی می‌شوند. درواقع تبدیل یه کاراکتر به حروف کوچیک(یا بزرگ) رو میشه به شکل یه تابع هش در نظر گرفت که یه سری collision داره و این collision ها باعث یکی شدن دوتا ایمیل متفاوت میشن، برای مثال جواب این شرط همیشه True هست.&amp;quoti&amp;quot.upper() == &amp;quotı&amp;quot.upper()  # Trueاینجا میتونید یه لیست از کاراکتر هایی که توی بزرگ یا کوچیک شدن یکی میشن رو ببینید.راه جلوگیری از باگبرای فرستادن ایمیل بازیابی پسوورد سه تا راه وجود داره:۱- ایمیل رو به آدرسی که توی دیتابیس ذخیره کردیم(ایمیل اصلی کاربر) بفرستیم۲- ایمیل رو به ایمیلی که حمله کننده وارد میکنه و به صورت uppercase یا lowercase درآوردیم بفرستیم۳- ایمیل رو به ایمیلی که حمله کننده وارد کرده و تغییری توش ندادیم بفرستیمراه حل اول به نظر راه درست هست  و جنگو هم همین کار رو انجام داده تا جلوی این حمله رو بگیره.این کامیتی هست که این باگ رو برطرف کرده و بهش نگاه بندازیم:active_users = UserModel._default_manager.filter(**{
    &#039;%s__iexact&#039; % email_field_name: email,
     &#039;is_active&#039;: True,
})توی این خط جنگو ایمیلی که برای بازیابی پسوورد ایمیل فرستاده شده رو بدون درنظر گرفتن بزرگی  و کوچیکی حروف(case insensitive) توی دیتابیس پیدا میکنه.در ادامه جنگو قبلا از همین متغیر email استفاده می‌کرده تا ایمیل بازیابی پسوورد رو بفرسته اما توی این کامیت این خط عوض شده و ایمیل به ایمیلی که کاربر توی دیتابیس داره فرستاده می‌شه:self.send_mail(subject_template_name, email_template_name, context, from_email,
-     email, html_email_template_name=html_email_template_name,
+    user_email, html_email_template_name=html_email_template_name, 
)توضیح: توی کد جدید به جای email از user_email استفاده شده که ایمیل توی دیتابیس هست.چه ورژن هایی تحت تأثیر بودن؟Django 3.0Django 2.2Django 1.11پس اگر از این ورژن ها استفاده می‌کنید جنگو رو اپدیت کنید. :)باگی مشابه در سایت Githubاین مشکل فقط برای جنگو پیش نیومده و قبلا هم توی گیت هاب هم بوده؛ نتیجه این هست که همیشه موقع کار با string ها باید مراقب بود و بدونیم که چیکار میکنیم.لینک توضیح کشف این باگ در سایت bug bounty گیت هاب.تابحال روی یک مشکل امنیتی دقیق نشده بودم ولی از روی همین باگ که با چند خط تغییر رفع می‌شد چیزهایی یادگرفتم که بلد نبودم. اگر نظری برای بهتر شدن نوشته دارید توی کامنت بنویسید یا اگر از اون خوشتون اومده ❤️ کنید. </description>
                <category>شایگان هوشیاری</category>
                <author>شایگان هوشیاری</author>
                <pubDate>Thu, 06 Feb 2020 21:08:21 +0330</pubDate>
            </item>
                    <item>
                <title>ریفکتور کردن پروژه جنگو: جابجایی مدل ها</title>
                <link>https://virgool.io/@sh.hooshyari/%D8%B1%DB%8C%D9%81%DA%A9%D8%AA%D9%88%D8%B1-%DA%A9%D8%B1%D8%AF%D9%86-%D9%BE%D8%B1%D9%88%DA%98%D9%87-%D8%AC%D9%86%DA%AF%D9%88-%D8%AC%D8%A7%D8%A8%D8%AC%D8%A7%DB%8C%DB%8C-%D9%85%D8%AF%D9%84-%D9%87%D8%A7-fnyvmfcxmw26</link>
                <description>این نوشته رو روی مدیوم منتشر کرده بودم حالا تصمیم گرفتم روی ویرگول هم بنویسم.Photo by Chris Ried on Unsplash  ریفکتور کردن کد بخش مهمی از توسعه ی نرم افزار هست که باعث میشه کد ما ساده تر و قابل فهم تر بشه و  توسعه ی دوباره ی اون رو راحت تر کنه. https://cdn-images-1.medium.com/max/800/1*ZXixptvL4rzkx3EDuj38xw.jpeg این موضوعی بود که چند وقت پیش باهاش روبرو شدم، منابع خیلی مناسبی واسه ی این راه حل پیدا نکردم و حتی توی استک اورفلو هم جواب خوبی پیدا نکردم(!) واسه ی مشکلی که داشتم. به خاطر همین بعد از حل کردن مشکل تصمیم گرفتم درباره ی راه حل این مسئله بنویسم. بریم سراغ جابجایی مدل ها!توی مشکل من همچین مدل هایی وجود داشتن:#app1 
class ChildModel(ParentModel):
  title = models.CharField(max_length=20)class OtherModel(models.Model):
  relation = models.Foreinkey(ChildModel, on_delete=models.SET_NULL)#app2
class ParentModel(models.Model):
  name = models.CharField(max_length=20) کاری که قرار بود انجام بشه جابجایی ParrentModel از app2 به app1 بود، روند اصلی این کار اینطوریه:۱- تغییر اسم تیبل ParentModel به اپ مقصد، چون جنگو اسم تیبل هارو به این شکل &#x27;app1_childmodel&#x27; نگه میداره پس وقتی اپ یه مدل عوض میشه باید اسم تیبل رو هم تغییر بدیم تا با ساختار جدید همخوانی داشته باشه۲- نوشتن migration برای جابجایی ParentModel به app1۳- اپدیت کردن foreignkeys و رابطه ها۴- نوشتن migration برای پاک کردن ParentModel از app2۵- جابجا کردن کد مدلتغییر اسم تیبلبرای این کار باید از meta.db_table استفاده کنیم، به این صورت:#app2
ParentModel(models.Model):
    #fields
    class Meta:
        db_table = &quot;app1_parentmodel&quot;جابجا کردن ParrentModel به app1این اون قسمتیه که یکمی تریکیه، باید تابعی بنویسیم که اونو با کامند RunPython اجرا کنیم و ParentModel رو به app1 ببره ولی مشکل اینجاست که چون ChildModel به ParentModel وابسته هست به خاطر ارث بری توی این مایگریشن اطلاعات کلاس پدر رو از دست میده و به این اررور میرسیم:django.db.migrations.state.InvalidBasesError: Cannot resolve bases for [&lt;ModelState: &#039;app1.ChildModel&#039;&gt;]
This can happen if you are inheriting models from an app with migrations (e.g. contrib.auth)-چیزی که فهمیدم این بود که اگر ChildModel رو از state اپ پاک کنیم و این تغییرات رو انجام بدیم بعد دوباره ChildModel رو برگردونیم بدون هیچ اروری migration ها اجرا میشن، الان میشه فهمید که پس اون foreignkey هم به همین مشکل میخوره چون که وقتی ChildModel حذف بشه(موقت)‌اون هم رفرنس رو از دست میده و احتمالاً حدس میزنید که اون فیلد رو هم از state پاک میکنم، حالا بریم ببینیم state چیه؟!مدل های جنگو ساختار دیتابیس رو هم برای جنگو تعریف میکنه هم برای دیتابیس و به شما این اجازه رو میده که شما تو هر migration مشخص کنید که آیا این تغییرات باید روی دیتابیس هم انجام بشه یا نه این کارو با استفاده از SeprateDataBaseAndState انجام میدیماین روشی هست که توی اون ChildModel و Foreignkey رو از State پاک میکنیم.#app1 0001 migration
from django.db import migrationsclass Migration(migrations.Migration):dependencies = [
        (&#039;app2&#039;,&#039;0001_renaming_the_table&#039;),
    ]state_operations = [
        migrations.RemoveField(
            model_name=&#039;othermodel&#039;,
            name=&#039;relation&#039;,
        ),
        migrations.DeleteModel(&#039;ChildModel&#039;),
    ]operations = [ migrations.SeparateDatabaseAndState(state_operations=state_operations)
    ]توی این migration هیچ دیتایی از دست نمیره اما از نظر جنگو این مدل و فیلد پاک شدن.حالا میتونیم migration رو برای جابجا کردن ParentModel  به app1 بنویسیمfrom django.db import migrations, modelsdef update_contentypes(apps, schema_editor):
    &quot;&quot;&quot;
    Updates content types.
    We want to have the same content type id, when the model is moved.
    &quot;&quot;&quot;
    ContentType = apps.get_model(&#039;contenttypes&#039;, &#039;ContentType&#039;)
    db_alias = schema_editor.connection.alias# Move the ParentModel to app1
    qs = ContentType.objects.using(db_alias).filter(app_label=&#039;app2&#039;, model=&#039;parentmodel&#039;)
    qs.update(app_label=&#039;app1&#039;)def update_contentypes_reverse(apps, schema_editor):
    &quot;&quot;&quot;
    Reverts changes in content types.
    &quot;&quot;&quot;
    ContentType = apps.get_model(&#039;contenttypes&#039;, &#039;ContentType&#039;)
    db_alias = schema_editor.connection.alias# Move the TrackingAlert model to tracking
    qs = ContentType.objects.using(db_alias).filter(app_label=&#039;app1&#039;, model=&#039;parentmodel&#039;)
    qs.update(app_label=&#039;app2&#039;)class Migration(migrations.Migration):dependencies = [
        (&#039;app1&#039;, &#039;0001_delete_from_state&#039;),
        (&#039;app2&#039;, &#039;0001_renaming_table&#039;),
    ]state_operations = [
        migrations.CreateModel(
            name=&#039;ParentModel&#039;,
            fields=[
                (&#039;id&#039;, models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name=&#039;ID&#039;)),
                #all the other fields
            ],
        ),
    ]database_operations = [
        migrations.RunPython(update_contentypes, update_contentypes_reverse),
    ]operations = [
        migrations.SeparateDatabaseAndState(
            state_operations=state_operations,
            database_operations=database_operations
        ),
    ]
توی متد CreateModel حتماً باید مدل رو همونطوری که توی کد هست تعریف کنیم تا ناهماهنگی پیش نیاد.اپدیت کردن ForeignKey ها و روابطتوی این حالتی که من بررسی کردم Foreignkey ای نداشتیم که به ParentModel اشاره کنه، ولی شما ممکنه داشته باشید پس راهشو توضیح میدم.باید برای هر مدلی که دارای این فیلد ForeignKey به مدل ParentModel باشه یه مایگریشن درست کنیم و توی اون با AlterTable مکانی که ParentModel توش هست رو عوض کنیم مثل این:from django.db import migrations, modelsclass Migration(migrations.Migration):dependencies = [
        (&#039;app1&#039;, &#039;0002_move_parent_model&#039;),
        # other dependencies
    ]state_operations = [
        migrations.AlterField(
            model_name=&#039;somemodel&#039;,
            name=&#039;theforeignkeyfield&#039;,
            field=models.ForeignKey(on_delete=models.deletion.CASCADE,
                                    to=&#039;app1.ParentModel&#039;),
        ),
    ]operations = [     migrations.SeparateDatabaseAndState(state_operations=state_operations)
    ]بعد از اپدیت کردن ریلیشن ها آماده ایم که کد مدل رو جابجا کنیم(yay!)نوشتن مایگریشن برای پاک کردن مدل ParentModel از app2چرا این مرحله باید انجام بشه؟ چون گفتم که جنگو یه اطلاعات از ساختار دیتابیس شما داره که اون رو اعمال میکنه روی دیتابیس ما نمیخوایم که مدل توی دیتابیس تغییر بکنه(پاک بشه) ولی میخوایم جنگو فکر کنه این مدل از این اپ پاک شده و به app2 رفته پس این مایگریشن رو مینویسم:class Migration(migrations.Migration):dependencies = [
        (&#039;app2&#039;, &#039;0001_renaming_table&#039;),
    ]state_operations = [
        migrations.DeleteModel(&#039;ParentModel&#039;),
    ]operations = [
        migrations.SeparateDatabaseAndState(state_operations=state_operations)
    ]این مرحله هم تموم شد میریم مرحله بعدیجابجا کردن کد مدلحالا کد مدل رو میبرم توی اون اپ و import هایی که داریم رو هم درست میکنیم.بعد از این میتونیم اون فیلد و مدلی که اول کار یه جورایی به جنگو گفتیم که وجود ندارند رو دوباره برگردونیم که کار سختی نیست با CreateModel و Addfield و SeparateDatabaseAndState انجامش میدیم:from django.db import migrations, models
import djangoclass Migration(migrations.Migration):dependencies = [
        (&#039;app1&#039;, &#039;0002_move_parent_model&#039;),
    ]state_operations = [
        migrations.CreateModel(
            name=&#039;ChildModel&#039;,
            fields=[
                (&#039;parentmodel_ptr&#039;,
                 models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True,
                                      primary_key=True, serialize=False, to=&#039;app1.ParentModel&#039;)),
                (&#039;title&#039;,models.CharField(max_length=20,),
    ]
        bases=(&#039;app1.parentmodel&#039;,))           migrations.AddField(
            model_name=&#039;othermodel&#039;,
            name=&#039;relation&#039;,
            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_Null), to=&#039;app1.ChildModel&#039;)
        ),operations = [
migrations.SeparateDatabaseAndState(state_operations=state_operations)
    ]و.. تموم شد. من این روش رو روی دیتابیس های SQlite و Psql تست کردم و به خوبی جواب داده اگر باز هم به مشکلی خوردید میتونید با من در ارتباط باشید.امیدوارم مفید بوده باشه.</description>
                <category>شایگان هوشیاری</category>
                <author>شایگان هوشیاری</author>
                <pubDate>Fri, 04 Jan 2019 12:21:53 +0330</pubDate>
            </item>
            </channel>
</rss>